modal 0.68.40__py3-none-any.whl → 0.68.49__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/_runtime/asgi.py +11 -4
- modal/_utils/function_utils.py +20 -12
- modal/client.pyi +2 -2
- modal/dict.py +1 -10
- modal/dict.pyi +0 -4
- modal/functions.py +1 -1
- modal/gpu.py +22 -0
- modal/image.py +0 -15
- modal/image.pyi +0 -26
- modal/mount.py +7 -4
- modal/network_file_system.py +1 -11
- modal/network_file_system.pyi +0 -4
- modal/partial_function.py +0 -6
- modal/queue.py +1 -10
- modal/queue.pyi +0 -4
- modal/volume.py +0 -9
- modal/volume.pyi +0 -4
- {modal-0.68.40.dist-info → modal-0.68.49.dist-info}/METADATA +2 -2
- {modal-0.68.40.dist-info → modal-0.68.49.dist-info}/RECORD +31 -31
- modal_proto/api.proto +9 -0
- modal_proto/api_grpc.py +16 -0
- modal_proto/api_pb2.py +785 -765
- modal_proto/api_pb2.pyi +30 -0
- modal_proto/api_pb2_grpc.py +33 -0
- modal_proto/api_pb2_grpc.pyi +10 -0
- modal_proto/modal_api_grpc.py +1 -0
- modal_version/_version_generated.py +1 -1
- {modal-0.68.40.dist-info → modal-0.68.49.dist-info}/LICENSE +0 -0
- {modal-0.68.40.dist-info → modal-0.68.49.dist-info}/WHEEL +0 -0
- {modal-0.68.40.dist-info → modal-0.68.49.dist-info}/entry_points.txt +0 -0
- {modal-0.68.40.dist-info → modal-0.68.49.dist-info}/top_level.txt +0 -0
modal/_runtime/asgi.py
CHANGED
@@ -26,6 +26,7 @@ class LifespanManager:
|
|
26
26
|
shutdown: asyncio.Future
|
27
27
|
queue: asyncio.Queue
|
28
28
|
has_run_init: bool = False
|
29
|
+
lifespan_supported: bool = False
|
29
30
|
|
30
31
|
def __init__(self, asgi_app, state):
|
31
32
|
self.asgi_app = asgi_app
|
@@ -46,6 +47,7 @@ class LifespanManager:
|
|
46
47
|
await self.ensure_init()
|
47
48
|
|
48
49
|
async def receive():
|
50
|
+
self.lifespan_supported = True
|
49
51
|
return await self.queue.get()
|
50
52
|
|
51
53
|
async def send(message):
|
@@ -63,16 +65,21 @@ class LifespanManager:
|
|
63
65
|
try:
|
64
66
|
await self.asgi_app({"type": "lifespan", "state": self.state}, receive, send)
|
65
67
|
except Exception as e:
|
68
|
+
if not self.lifespan_supported:
|
69
|
+
logger.info(f"ASGI lifespan task exited before receiving any messages with exception:\n{e}")
|
70
|
+
self.startup.set_result(None)
|
71
|
+
self.shutdown.set_result(None)
|
72
|
+
return
|
73
|
+
|
66
74
|
logger.error(f"Error in ASGI lifespan task: {e}")
|
67
75
|
if not self.startup.done():
|
68
76
|
self.startup.set_exception(ExecutionError("ASGI lifespan task exited startup"))
|
69
77
|
if not self.shutdown.done():
|
70
78
|
self.shutdown.set_exception(ExecutionError("ASGI lifespan task exited shutdown"))
|
71
79
|
else:
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
self.shutdown.set_result("ASGI Lifespan protocol is probably not supported by this library")
|
80
|
+
logger.info("ASGI Lifespan protocol is probably not supported by this library")
|
81
|
+
self.startup.set_result(None)
|
82
|
+
self.shutdown.set_result(None)
|
76
83
|
|
77
84
|
async def lifespan_startup(self):
|
78
85
|
await self.ensure_init()
|
modal/_utils/function_utils.py
CHANGED
@@ -99,16 +99,18 @@ def get_function_type(is_generator: Optional[bool]) -> "api_pb2.Function.Functio
|
|
99
99
|
|
100
100
|
|
101
101
|
class FunctionInfo:
|
102
|
-
"""
|
102
|
+
"""Utility that determines serialization/deserialization mechanisms for functions
|
103
103
|
|
104
|
-
|
105
|
-
|
104
|
+
* Stored as file vs serialized
|
105
|
+
* If serialized: how to serialize the function
|
106
|
+
* If file: which module/function name should be used to retrieve
|
107
|
+
|
108
|
+
Used for populating the definition of a remote function
|
106
109
|
"""
|
107
110
|
|
108
111
|
raw_f: Optional[Callable[..., Any]] # if None - this is a "class service function"
|
109
112
|
function_name: str
|
110
113
|
user_cls: Optional[type[Any]]
|
111
|
-
definition_type: "modal_proto.api_pb2.Function.DefinitionType.ValueType"
|
112
114
|
module_name: Optional[str]
|
113
115
|
|
114
116
|
_type: FunctionInfoType
|
@@ -116,6 +118,12 @@ class FunctionInfo:
|
|
116
118
|
_base_dir: str
|
117
119
|
_remote_dir: Optional[PurePosixPath] = None
|
118
120
|
|
121
|
+
def get_definition_type(self) -> "modal_proto.api_pb2.Function.DefinitionType.ValueType":
|
122
|
+
if self.is_serialized():
|
123
|
+
return modal_proto.api_pb2.Function.DEFINITION_TYPE_SERIALIZED
|
124
|
+
else:
|
125
|
+
return modal_proto.api_pb2.Function.DEFINITION_TYPE_FILE
|
126
|
+
|
119
127
|
def is_service_class(self):
|
120
128
|
if self.raw_f is None:
|
121
129
|
assert self.user_cls
|
@@ -172,7 +180,7 @@ class FunctionInfo:
|
|
172
180
|
self._base_dir = base_dirs[0]
|
173
181
|
self.module_name = module.__spec__.name
|
174
182
|
self._remote_dir = ROOT_DIR / PurePosixPath(module.__package__.split(".")[0])
|
175
|
-
self.
|
183
|
+
self._is_serialized = False
|
176
184
|
self._type = FunctionInfoType.PACKAGE
|
177
185
|
elif hasattr(module, "__file__") and not serialized:
|
178
186
|
# This generally covers the case where it's invoked with
|
@@ -182,18 +190,18 @@ class FunctionInfo:
|
|
182
190
|
self._file = os.path.abspath(inspect.getfile(module))
|
183
191
|
self.module_name = inspect.getmodulename(self._file)
|
184
192
|
self._base_dir = os.path.dirname(self._file)
|
185
|
-
self.
|
193
|
+
self._is_serialized = False
|
186
194
|
self._type = FunctionInfoType.FILE
|
187
195
|
else:
|
188
196
|
self.module_name = None
|
189
197
|
self._base_dir = os.path.abspath("") # get current dir
|
190
|
-
self.
|
191
|
-
if serialized:
|
198
|
+
self._is_serialized = True # either explicitly, or by being in a notebook
|
199
|
+
if serialized: # if explicit
|
192
200
|
self._type = FunctionInfoType.SERIALIZED
|
193
201
|
else:
|
194
202
|
self._type = FunctionInfoType.NOTEBOOK
|
195
203
|
|
196
|
-
if self.
|
204
|
+
if not self.is_serialized():
|
197
205
|
# Sanity check that this function is defined in global scope
|
198
206
|
# Unfortunately, there's no "clean" way to do this in Python
|
199
207
|
qualname = f.__qualname__ if f else user_cls.__qualname__
|
@@ -203,7 +211,7 @@ class FunctionInfo:
|
|
203
211
|
)
|
204
212
|
|
205
213
|
def is_serialized(self) -> bool:
|
206
|
-
return self.
|
214
|
+
return self._is_serialized
|
207
215
|
|
208
216
|
def serialized_function(self) -> bytes:
|
209
217
|
# Note: this should only be called from .load() and not at function decoration time
|
@@ -312,7 +320,7 @@ class FunctionInfo:
|
|
312
320
|
if self._type == FunctionInfoType.PACKAGE:
|
313
321
|
if config.get("automount"):
|
314
322
|
return [_Mount.from_local_python_packages(self.module_name)]
|
315
|
-
elif self.
|
323
|
+
elif not self.is_serialized():
|
316
324
|
# mount only relevant file and __init__.py:s
|
317
325
|
return [
|
318
326
|
_Mount.from_local_dir(
|
@@ -322,7 +330,7 @@ class FunctionInfo:
|
|
322
330
|
condition=entrypoint_only_package_mount_condition(self._file),
|
323
331
|
)
|
324
332
|
]
|
325
|
-
elif self.
|
333
|
+
elif not self.is_serialized():
|
326
334
|
remote_path = ROOT_DIR / Path(self._file).name
|
327
335
|
if not _is_modal_path(remote_path):
|
328
336
|
return [
|
modal/client.pyi
CHANGED
@@ -26,7 +26,7 @@ class _Client:
|
|
26
26
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
27
27
|
|
28
28
|
def __init__(
|
29
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.
|
29
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.49"
|
30
30
|
): ...
|
31
31
|
def is_closed(self) -> bool: ...
|
32
32
|
@property
|
@@ -81,7 +81,7 @@ class Client:
|
|
81
81
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
82
82
|
|
83
83
|
def __init__(
|
84
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.
|
84
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.49"
|
85
85
|
): ...
|
86
86
|
def is_closed(self) -> bool: ...
|
87
87
|
@property
|
modal/dict.py
CHANGED
@@ -10,7 +10,7 @@ from modal_proto import api_pb2
|
|
10
10
|
from ._resolver import Resolver
|
11
11
|
from ._serialization import deserialize, serialize
|
12
12
|
from ._utils.async_utils import TaskContext, synchronize_api
|
13
|
-
from ._utils.deprecation import
|
13
|
+
from ._utils.deprecation import renamed_parameter
|
14
14
|
from ._utils.grpc_utils import retry_transient_errors
|
15
15
|
from ._utils.name_utils import check_object_name
|
16
16
|
from .client import _Client
|
@@ -58,15 +58,6 @@ class _Dict(_Object, type_prefix="di"):
|
|
58
58
|
For more examples, see the [guide](/docs/guide/dicts-and-queues#modal-dicts).
|
59
59
|
"""
|
60
60
|
|
61
|
-
@staticmethod
|
62
|
-
def new(data: Optional[dict] = None):
|
63
|
-
"""mdmd:hidden"""
|
64
|
-
message = (
|
65
|
-
"`Dict.new` is deprecated."
|
66
|
-
" Please use `Dict.from_name` (for persisted) or `Dict.ephemeral` (for ephemeral) dicts instead."
|
67
|
-
)
|
68
|
-
deprecation_error((2024, 3, 19), message)
|
69
|
-
|
70
61
|
def __init__(self, data={}):
|
71
62
|
"""mdmd:hidden"""
|
72
63
|
raise RuntimeError(
|
modal/dict.pyi
CHANGED
@@ -8,8 +8,6 @@ import typing_extensions
|
|
8
8
|
def _serialize_dict(data): ...
|
9
9
|
|
10
10
|
class _Dict(modal.object._Object):
|
11
|
-
@staticmethod
|
12
|
-
def new(data: typing.Optional[dict] = None): ...
|
13
11
|
def __init__(self, data={}): ...
|
14
12
|
@classmethod
|
15
13
|
def ephemeral(
|
@@ -60,8 +58,6 @@ class _Dict(modal.object._Object):
|
|
60
58
|
|
61
59
|
class Dict(modal.object.Object):
|
62
60
|
def __init__(self, data={}): ...
|
63
|
-
@staticmethod
|
64
|
-
def new(data: typing.Optional[dict] = None): ...
|
65
61
|
@classmethod
|
66
62
|
def ephemeral(
|
67
63
|
cls: type[Dict],
|
modal/functions.py
CHANGED
@@ -753,7 +753,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
753
753
|
mount_ids=loaded_mount_ids,
|
754
754
|
secret_ids=[secret.object_id for secret in secrets],
|
755
755
|
image_id=(image.object_id if image else ""),
|
756
|
-
definition_type=info.
|
756
|
+
definition_type=info.get_definition_type(),
|
757
757
|
function_serialized=function_serialized or b"",
|
758
758
|
class_serialized=class_serialized or b"",
|
759
759
|
function_type=function_type,
|
modal/gpu.py
CHANGED
@@ -137,6 +137,27 @@ class H100(_GPUConfig):
|
|
137
137
|
return f"GPU(H100, count={self.count})"
|
138
138
|
|
139
139
|
|
140
|
+
class L40S(_GPUConfig):
|
141
|
+
"""
|
142
|
+
[NVIDIA L40S](https://www.nvidia.com/en-us/data-center/l40s/) GPU class.
|
143
|
+
|
144
|
+
The L40S is a data center GPU for the Ada Lovelace architecture. It has 48 GB of on-chip
|
145
|
+
GDDR6 RAM and enhanced support for FP8 precision.
|
146
|
+
"""
|
147
|
+
|
148
|
+
def __init__(
|
149
|
+
self,
|
150
|
+
*,
|
151
|
+
# Number of GPUs per container. Defaults to 1.
|
152
|
+
# Useful if you have very large models that don't fit on a single GPU.
|
153
|
+
count: int = 1,
|
154
|
+
):
|
155
|
+
super().__init__(api_pb2.GPU_TYPE_L40S, count)
|
156
|
+
|
157
|
+
def __repr__(self):
|
158
|
+
return f"GPU(L40S, count={self.count})"
|
159
|
+
|
160
|
+
|
140
161
|
class Any(_GPUConfig):
|
141
162
|
"""Selects any one of the GPU classes available within Modal, according to availability."""
|
142
163
|
|
@@ -154,6 +175,7 @@ STRING_TO_GPU_CONFIG: dict[str, Callable] = {
|
|
154
175
|
"a100-80gb": lambda: A100(size="80GB"),
|
155
176
|
"h100": H100,
|
156
177
|
"a10g": A10G,
|
178
|
+
"l40s": L40S,
|
157
179
|
"any": Any,
|
158
180
|
}
|
159
181
|
display_string_to_config = "\n".join(f'- "{key}" → `{c()}`' for key, c in STRING_TO_GPU_CONFIG.items() if key != "inf2")
|
modal/image.py
CHANGED
@@ -571,21 +571,6 @@ class _Image(_Object, type_prefix="im"):
|
|
571
571
|
obj.force_build = force_build
|
572
572
|
return obj
|
573
573
|
|
574
|
-
def extend(self, **kwargs) -> "_Image":
|
575
|
-
"""mdmd:hidden"""
|
576
|
-
deprecation_error(
|
577
|
-
(2024, 3, 7),
|
578
|
-
"`Image.extend` is deprecated; please use a higher-level method, such as `Image.dockerfile_commands`.",
|
579
|
-
)
|
580
|
-
|
581
|
-
def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
|
582
|
-
return DockerfileSpec(
|
583
|
-
commands=kwargs.pop("dockerfile_commands", []),
|
584
|
-
context_files=kwargs.pop("context_files", {}),
|
585
|
-
)
|
586
|
-
|
587
|
-
return _Image._from_args(base_images={"base": self}, dockerfile_function=build_dockerfile, **kwargs)
|
588
|
-
|
589
574
|
def copy_mount(self, mount: _Mount, remote_path: Union[str, Path] = ".") -> "_Image":
|
590
575
|
"""Copy the entire contents of a `modal.Mount` into an image.
|
591
576
|
Useful when files only available locally are required during the image
|
modal/image.pyi
CHANGED
@@ -92,19 +92,6 @@ class _Image(modal.object._Object):
|
|
92
92
|
_namespace: int = 1,
|
93
93
|
_do_assert_no_mount_layers: bool = True,
|
94
94
|
): ...
|
95
|
-
def extend(
|
96
|
-
self,
|
97
|
-
*,
|
98
|
-
secrets: typing.Optional[collections.abc.Sequence[modal.secret._Secret]] = None,
|
99
|
-
gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
|
100
|
-
build_function: typing.Optional[modal.functions._Function] = None,
|
101
|
-
build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
|
102
|
-
image_registry_config: typing.Optional[_ImageRegistryConfig] = None,
|
103
|
-
context_mount: typing.Optional[modal.mount._Mount] = None,
|
104
|
-
force_build: bool = False,
|
105
|
-
_namespace: int = 1,
|
106
|
-
_do_assert_no_mount_layers: bool = True,
|
107
|
-
) -> _Image: ...
|
108
95
|
def copy_mount(self, mount: modal.mount._Mount, remote_path: typing.Union[str, pathlib.Path] = ".") -> _Image: ...
|
109
96
|
def add_local_file(
|
110
97
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: str, *, copy: bool = False
|
@@ -364,19 +351,6 @@ class Image(modal.object.Object):
|
|
364
351
|
_namespace: int = 1,
|
365
352
|
_do_assert_no_mount_layers: bool = True,
|
366
353
|
): ...
|
367
|
-
def extend(
|
368
|
-
self,
|
369
|
-
*,
|
370
|
-
secrets: typing.Optional[collections.abc.Sequence[modal.secret.Secret]] = None,
|
371
|
-
gpu_config: typing.Optional[modal_proto.api_pb2.GPUConfig] = None,
|
372
|
-
build_function: typing.Optional[modal.functions.Function] = None,
|
373
|
-
build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
|
374
|
-
image_registry_config: typing.Optional[_ImageRegistryConfig] = None,
|
375
|
-
context_mount: typing.Optional[modal.mount.Mount] = None,
|
376
|
-
force_build: bool = False,
|
377
|
-
_namespace: int = 1,
|
378
|
-
_do_assert_no_mount_layers: bool = True,
|
379
|
-
) -> Image: ...
|
380
354
|
def copy_mount(self, mount: modal.mount.Mount, remote_path: typing.Union[str, pathlib.Path] = ".") -> Image: ...
|
381
355
|
def add_local_file(
|
382
356
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: str, *, copy: bool = False
|
modal/mount.py
CHANGED
@@ -105,7 +105,7 @@ class _MountFile(_MountEntry):
|
|
105
105
|
return str(self.local_file)
|
106
106
|
|
107
107
|
def get_files_to_upload(self):
|
108
|
-
local_file = self.local_file.
|
108
|
+
local_file = self.local_file.resolve()
|
109
109
|
if not local_file.exists():
|
110
110
|
raise FileNotFoundError(local_file)
|
111
111
|
|
@@ -131,6 +131,8 @@ class _MountDir(_MountEntry):
|
|
131
131
|
return str(self.local_dir.expanduser().absolute())
|
132
132
|
|
133
133
|
def get_files_to_upload(self):
|
134
|
+
# we can't use .resolve() eagerly here since that could end up "renaming" symlinked files
|
135
|
+
# see test_mount_directory_with_symlinked_file
|
134
136
|
local_dir = self.local_dir.expanduser().absolute()
|
135
137
|
|
136
138
|
if not local_dir.exists():
|
@@ -145,10 +147,11 @@ class _MountDir(_MountEntry):
|
|
145
147
|
gen = (dir_entry.path for dir_entry in os.scandir(local_dir) if dir_entry.is_file())
|
146
148
|
|
147
149
|
for local_filename in gen:
|
148
|
-
|
149
|
-
|
150
|
+
local_path = Path(local_filename)
|
151
|
+
if not self.ignore(local_path):
|
152
|
+
local_relpath = local_path.expanduser().absolute().relative_to(local_dir)
|
150
153
|
mount_path = self.remote_path / local_relpath.as_posix()
|
151
|
-
yield
|
154
|
+
yield local_path.resolve(), mount_path
|
152
155
|
|
153
156
|
def watch_entry(self):
|
154
157
|
return self.local_dir.resolve().expanduser(), None
|
modal/network_file_system.py
CHANGED
@@ -15,7 +15,7 @@ from modal_proto import api_pb2
|
|
15
15
|
from ._resolver import Resolver
|
16
16
|
from ._utils.async_utils import TaskContext, aclosing, async_map, sync_or_async_iter, synchronize_api
|
17
17
|
from ._utils.blob_utils import LARGE_FILE_LIMIT, blob_iter, blob_upload_file
|
18
|
-
from ._utils.deprecation import
|
18
|
+
from ._utils.deprecation import renamed_parameter
|
19
19
|
from ._utils.grpc_utils import retry_transient_errors
|
20
20
|
from ._utils.hash_utils import get_sha256_hex
|
21
21
|
from ._utils.name_utils import check_object_name
|
@@ -90,16 +90,6 @@ class _NetworkFileSystem(_Object, type_prefix="sv"):
|
|
90
90
|
```
|
91
91
|
"""
|
92
92
|
|
93
|
-
@staticmethod
|
94
|
-
def new(cloud: Optional[str] = None):
|
95
|
-
"""mdmd:hidden"""
|
96
|
-
message = (
|
97
|
-
"`NetworkFileSystem.new` is deprecated."
|
98
|
-
" Please use `NetworkFileSystem.from_name` (for persisted)"
|
99
|
-
" or `NetworkFileSystem.ephemeral` (for ephemeral) network filesystems instead."
|
100
|
-
)
|
101
|
-
deprecation_error((2024, 3, 20), message)
|
102
|
-
|
103
93
|
@staticmethod
|
104
94
|
@renamed_parameter((2024, 12, 18), "label", "name")
|
105
95
|
def from_name(
|
modal/network_file_system.pyi
CHANGED
@@ -13,8 +13,6 @@ def network_file_system_mount_protos(
|
|
13
13
|
) -> list[modal_proto.api_pb2.SharedVolumeMount]: ...
|
14
14
|
|
15
15
|
class _NetworkFileSystem(modal.object._Object):
|
16
|
-
@staticmethod
|
17
|
-
def new(cloud: typing.Optional[str] = None): ...
|
18
16
|
@staticmethod
|
19
17
|
def from_name(
|
20
18
|
name: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
@@ -71,8 +69,6 @@ class _NetworkFileSystem(modal.object._Object):
|
|
71
69
|
class NetworkFileSystem(modal.object.Object):
|
72
70
|
def __init__(self, *args, **kwargs): ...
|
73
71
|
@staticmethod
|
74
|
-
def new(cloud: typing.Optional[str] = None): ...
|
75
|
-
@staticmethod
|
76
72
|
def from_name(
|
77
73
|
name: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
78
74
|
) -> NetworkFileSystem: ...
|
modal/partial_function.py
CHANGED
@@ -611,12 +611,6 @@ def _exit(_warn_parentheses_missing=None) -> Callable[[ExitHandlerType], _Partia
|
|
611
611
|
if isinstance(f, _PartialFunction):
|
612
612
|
_disallow_wrapping_method(f, "exit")
|
613
613
|
|
614
|
-
if callable_has_non_self_params(f):
|
615
|
-
message = (
|
616
|
-
"Support for decorating parameterized methods with `@exit` has been deprecated."
|
617
|
-
" Please update your code by removing the parameters."
|
618
|
-
)
|
619
|
-
deprecation_error((2024, 2, 23), message)
|
620
614
|
return _PartialFunction(f, _PartialFunctionFlags.EXIT)
|
621
615
|
|
622
616
|
return wrapper
|
modal/queue.py
CHANGED
@@ -13,7 +13,7 @@ from modal_proto import api_pb2
|
|
13
13
|
from ._resolver import Resolver
|
14
14
|
from ._serialization import deserialize, serialize
|
15
15
|
from ._utils.async_utils import TaskContext, synchronize_api, warn_if_generator_is_not_consumed
|
16
|
-
from ._utils.deprecation import
|
16
|
+
from ._utils.deprecation import renamed_parameter
|
17
17
|
from ._utils.grpc_utils import retry_transient_errors
|
18
18
|
from ._utils.name_utils import check_object_name
|
19
19
|
from .client import _Client
|
@@ -94,15 +94,6 @@ class _Queue(_Object, type_prefix="qu"):
|
|
94
94
|
Partition keys must be non-empty and must not exceed 64 bytes.
|
95
95
|
"""
|
96
96
|
|
97
|
-
@staticmethod
|
98
|
-
def new():
|
99
|
-
"""mdmd:hidden"""
|
100
|
-
message = (
|
101
|
-
"`Queue.new` is deprecated."
|
102
|
-
" Please use `Queue.from_name` (for persisted) or `Queue.ephemeral` (for ephemeral) queues instead."
|
103
|
-
)
|
104
|
-
deprecation_error((2024, 3, 19), message)
|
105
|
-
|
106
97
|
def __init__(self):
|
107
98
|
"""mdmd:hidden"""
|
108
99
|
raise RuntimeError("Queue() is not allowed. Please use `Queue.from_name(...)` or `Queue.ephemeral()` instead.")
|
modal/queue.pyi
CHANGED
@@ -6,8 +6,6 @@ import typing
|
|
6
6
|
import typing_extensions
|
7
7
|
|
8
8
|
class _Queue(modal.object._Object):
|
9
|
-
@staticmethod
|
10
|
-
def new(): ...
|
11
9
|
def __init__(self): ...
|
12
10
|
@staticmethod
|
13
11
|
def validate_partition_key(partition: typing.Optional[str]) -> bytes: ...
|
@@ -89,8 +87,6 @@ class _Queue(modal.object._Object):
|
|
89
87
|
class Queue(modal.object.Object):
|
90
88
|
def __init__(self): ...
|
91
89
|
@staticmethod
|
92
|
-
def new(): ...
|
93
|
-
@staticmethod
|
94
90
|
def validate_partition_key(partition: typing.Optional[str]) -> bytes: ...
|
95
91
|
@classmethod
|
96
92
|
def ephemeral(
|
modal/volume.py
CHANGED
@@ -134,15 +134,6 @@ class _Volume(_Object, type_prefix="vo"):
|
|
134
134
|
self._lock = asyncio.Lock()
|
135
135
|
return self._lock
|
136
136
|
|
137
|
-
@staticmethod
|
138
|
-
def new():
|
139
|
-
"""mdmd:hidden"""
|
140
|
-
message = (
|
141
|
-
"`Volume.new` is deprecated."
|
142
|
-
" Please use `Volume.from_name` (for persisted) or `Volume.ephemeral` (for ephemeral) volumes instead."
|
143
|
-
)
|
144
|
-
deprecation_error((2024, 3, 20), message)
|
145
|
-
|
146
137
|
@staticmethod
|
147
138
|
@renamed_parameter((2024, 12, 18), "label", "name")
|
148
139
|
def from_name(
|
modal/volume.pyi
CHANGED
@@ -38,8 +38,6 @@ class _Volume(modal.object._Object):
|
|
38
38
|
|
39
39
|
async def _get_lock(self): ...
|
40
40
|
@staticmethod
|
41
|
-
def new(): ...
|
42
|
-
@staticmethod
|
43
41
|
def from_name(
|
44
42
|
name: str,
|
45
43
|
namespace=1,
|
@@ -133,8 +131,6 @@ class Volume(modal.object.Object):
|
|
133
131
|
|
134
132
|
_get_lock: ___get_lock_spec
|
135
133
|
|
136
|
-
@staticmethod
|
137
|
-
def new(): ...
|
138
134
|
@staticmethod
|
139
135
|
def from_name(
|
140
136
|
name: str,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: modal
|
3
|
-
Version: 0.68.
|
3
|
+
Version: 0.68.49
|
4
4
|
Summary: Python client library for Modal
|
5
5
|
Author: Modal Labs
|
6
6
|
Author-email: support@modal.com
|
@@ -21,7 +21,7 @@ Requires-Dist: fastapi
|
|
21
21
|
Requires-Dist: grpclib (==0.4.7)
|
22
22
|
Requires-Dist: protobuf (!=4.24.0,<6.0,>=3.19)
|
23
23
|
Requires-Dist: rich (>=12.0.0)
|
24
|
-
Requires-Dist: synchronicity (~=0.9.
|
24
|
+
Requires-Dist: synchronicity (~=0.9.8)
|
25
25
|
Requires-Dist: toml
|
26
26
|
Requires-Dist: typer (>=0.9)
|
27
27
|
Requires-Dist: types-certifi
|
@@ -19,7 +19,7 @@ modal/app.py,sha256=JWefPs4yB70BKQwSZejB_4_muhxn63cC9UmnNvpQ9XY,45526
|
|
19
19
|
modal/app.pyi,sha256=FYPCEJNhof4YF6HIuNP_2yG6s2PgZnKW9tO1hFE6sfA,25194
|
20
20
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
21
21
|
modal/client.py,sha256=JAnd4-GCN093BwkvOFAK5a6iy5ycxofjpUncMxlrIMw,15253
|
22
|
-
modal/client.pyi,sha256=
|
22
|
+
modal/client.pyi,sha256=Rik6-3dxB5m65vniRcLjBuLaYVlw6tNh8VStRzAD7eE,7280
|
23
23
|
modal/cloud_bucket_mount.py,sha256=G7T7jWLD0QkmrfKR75mSTwdUZ2xNfj7pkVqb4ipmxmI,5735
|
24
24
|
modal/cloud_bucket_mount.pyi,sha256=CEi7vrH3kDUF4LAy4qP6tfImy2UJuFRcRbsgRNM1wo8,1403
|
25
25
|
modal/cls.py,sha256=3hjb0JcoPjxKZNeK22f5rR43bZRBjoRI7_EMZXY7YrE,31172
|
@@ -27,8 +27,8 @@ modal/cls.pyi,sha256=ooDU2IaGgD5VQ3jDX2KCrqb3a91-AdKv8yKYSkjnW8Y,8230
|
|
27
27
|
modal/config.py,sha256=BzhZYUUwOmvVwf6x5kf0ywMC257s648dmuhsnB6g3gk,11041
|
28
28
|
modal/container_process.py,sha256=WTqLn01dJPVkPpwR_0w_JH96ceN5mV4TGtiu1ZR2RRA,6108
|
29
29
|
modal/container_process.pyi,sha256=dqtqBmyRpXXpRrDooESL6WBVU_1Rh6OG-66P2Hk9E5U,2666
|
30
|
-
modal/dict.py,sha256=
|
31
|
-
modal/dict.pyi,sha256=
|
30
|
+
modal/dict.py,sha256=ei9jsA5iTj4UFGPJxTAed6vjd49W47ezDtj0koUmVts,12497
|
31
|
+
modal/dict.pyi,sha256=VmbzxltA2vFlIHZCxpNGtd-ieXwcUwdw3iyy3WCweqU,7115
|
32
32
|
modal/environments.py,sha256=wbv9ttFCbzATGfwcmvYiG608PfHovx0AQmawsg-jmic,6660
|
33
33
|
modal/environments.pyi,sha256=rF7oaaELoSNuoD6qImGnIbuGPtgWwR5SlcExyYJ61hQ,3515
|
34
34
|
modal/exception.py,sha256=GEV6xMnVnkle0gsFZVLB4B7cUMyw8HzVDvAvPr34ZV4,5185
|
@@ -36,29 +36,29 @@ modal/experimental.py,sha256=jFuNbwrNHos47viMB9q-cHJSvf2RDxDdoEcss9plaZE,2302
|
|
36
36
|
modal/file_io.py,sha256=pDOFNQU5m-x-k3oJauck4fOp3bZ55Vc-_LvSaN5_Bow,16465
|
37
37
|
modal/file_io.pyi,sha256=GMhCCRyMftXYI3HqI9EdGPOx70CbCNi-VC5Sfy5TYnc,7631
|
38
38
|
modal/file_pattern_matcher.py,sha256=V6P74Vc7LAuBFe_uepIaZmoDJiuAvqjFibe0GcMJwxo,5119
|
39
|
-
modal/functions.py,sha256
|
39
|
+
modal/functions.py,sha256=aXXXr3rk7BCeh5OWMvxGksGm8FQoYCyrBDGV74FPoPE,67827
|
40
40
|
modal/functions.pyi,sha256=oMmcExtQxHwPej06jQ3uBe1tUlSR3VbAx7u3Vm-Ohhg,25317
|
41
|
-
modal/gpu.py,sha256=
|
42
|
-
modal/image.py,sha256
|
43
|
-
modal/image.pyi,sha256=
|
41
|
+
modal/gpu.py,sha256=MTxj6ql8EpgfBg8YmZ5a1cLznyuZFssX1qXbEX4LKVM,7503
|
42
|
+
modal/image.py,sha256=sv45bYaF5Jlmk8mQE3EDADYyXLi14hOe2CUM0Zb8Xao,82243
|
43
|
+
modal/image.pyi,sha256=VY_4HnDBhW8u_Zd3n-YBZ1H9idbTorWGwzsAzY7-B70,24213
|
44
44
|
modal/io_streams.py,sha256=QkQiizKRzd5bnbKQsap31LJgBYlAnj4-XkV_50xPYX0,15079
|
45
45
|
modal/io_streams.pyi,sha256=bCCVSxkMcosYd8O3PQDDwJw7TQ8JEcnYonLJ5t27TQs,4804
|
46
|
-
modal/mount.py,sha256=
|
46
|
+
modal/mount.py,sha256=Miu9V5LB80uoMasSXxxf0aYTC7H1G08PjnjmmjQdyRc,29346
|
47
47
|
modal/mount.pyi,sha256=7dKl_JeVka3g4oKw7D-FFRU-Zpadt9LJEcfNUnhj540,10491
|
48
|
-
modal/network_file_system.py,sha256=
|
49
|
-
modal/network_file_system.pyi,sha256=
|
48
|
+
modal/network_file_system.py,sha256=INj1TfN_Fsmabmlte7anvey1epodjbMmjBW_TIJSST4,14406
|
49
|
+
modal/network_file_system.pyi,sha256=61M-sdWrtaRjmuNVsvusI6kf1Qw-jUOVXvEAeOkM8Aw,7751
|
50
50
|
modal/object.py,sha256=HZs3N59C6JxlMuPQWJYvrWV1FEEkH9txUovVDorVUbs,9763
|
51
51
|
modal/object.pyi,sha256=MO78H9yFSE5i1gExPEwyyQzLdlshkcGHN1aQ0ylyvq0,8802
|
52
52
|
modal/output.py,sha256=N0xf4qeudEaYrslzdAl35VKV8rapstgIM2e9wO8_iy0,1967
|
53
53
|
modal/parallel_map.py,sha256=4aoMXIrlG3wl5Ifk2YDNOQkXsGRsm6Xbfm6WtJ2t3WY,16002
|
54
54
|
modal/parallel_map.pyi,sha256=pOhT0P3DDYlwLx0fR3PTsecA7DI8uOdXC1N8i-ZkyOY,2328
|
55
|
-
modal/partial_function.py,sha256=
|
55
|
+
modal/partial_function.py,sha256=pDDNR6KTaIIPpuKQaoO1vgP83_LTwxMhtOn6sVRrmC8,27862
|
56
56
|
modal/partial_function.pyi,sha256=pO6kf8i5HVsZ7CF0z_KkzLk4Aeq7NJhFJ_VNIycRXaU,9260
|
57
57
|
modal/proxy.py,sha256=ZrOsuQP7dSZFq1OrIxalNnt0Zvsnp1h86Th679sSL40,1417
|
58
58
|
modal/proxy.pyi,sha256=UvygdOYneLTuoDY6hVaMNCyZ947Tmx93IdLjErUqkvM,368
|
59
59
|
modal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
|
-
modal/queue.py,sha256=
|
61
|
-
modal/queue.pyi,sha256=
|
60
|
+
modal/queue.py,sha256=zMUQtdAyqZzBg-2iAo3c3G54HLP7TEWfVhiQXLjewb4,18556
|
61
|
+
modal/queue.pyi,sha256=gGV97pWelSSYqMV9Bl4ys3mSP7q82fS71oqSWeAwyDE,9818
|
62
62
|
modal/retries.py,sha256=HKR2Q9aNPWkMjQ5nwobqYTuZaSuw0a8lI2zrtY5IW98,5230
|
63
63
|
modal/runner.py,sha256=qfkB0OM97kb_-oP-D5KPj_jUwfd8ePUA3R_zLkjSTBQ,24586
|
64
64
|
modal/runner.pyi,sha256=BvMS1ZVzWSn8B8q0KnIZOJKPkN5L-i5b-USbV6SWWHQ,5177
|
@@ -74,10 +74,10 @@ modal/serving.pyi,sha256=ncV-9jY_vZYFnGs5ZnMb3ffrX8LmcLdIMHBC56xRbtE,1711
|
|
74
74
|
modal/stream_type.py,sha256=A6320qoAAWhEfwOCZfGtymQTu5AfLfJXXgARqooTPvY,417
|
75
75
|
modal/token_flow.py,sha256=LcgSce_MSQ2p7j55DPwpVRpiAtCDe8GRSEwzO7muNR8,6774
|
76
76
|
modal/token_flow.pyi,sha256=gOYtYujrWt_JFZeiI8EmfahXPx5GCR5Na-VaPQcWgEY,1937
|
77
|
-
modal/volume.py,sha256=
|
78
|
-
modal/volume.pyi,sha256=
|
77
|
+
modal/volume.py,sha256=T-pLxCYqmqRO6OolpAXlPxomMu0RWjti2e4kUpaj2cQ,29229
|
78
|
+
modal/volume.pyi,sha256=eekb2dnAAwFK_NO9ciAOOTthl8NP1iAmMFrCGgjDA2k,11100
|
79
79
|
modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
80
|
-
modal/_runtime/asgi.py,sha256=
|
80
|
+
modal/_runtime/asgi.py,sha256=Mjs859pSgOmtZL-YmEsSKN557v1A2Ax_5-ERgPfj55E,21920
|
81
81
|
modal/_runtime/container_io_manager.py,sha256=ctgyNFiHjq1brCrabXmlurkAXjnrCeWPRvTVa735vRw,44215
|
82
82
|
modal/_runtime/execution_context.py,sha256=E6ofm6j1POXGPxS841X3V7JU6NheVb8OkQc7JpLq4Kg,2712
|
83
83
|
modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5163
|
@@ -88,7 +88,7 @@ modal/_utils/async_utils.py,sha256=9ubwMkwiDB4gzOYG2jL9j7Fs-5dxHjcifZe3r7JRg-k,2
|
|
88
88
|
modal/_utils/blob_utils.py,sha256=N66LtZI8PpCkZ7maA7GLW5CAmYUoNJdG-GjaAUR4_NQ,14509
|
89
89
|
modal/_utils/bytes_io_segment_payload.py,sha256=uunxVJS4PE1LojF_UpURMzVK9GuvmYWRqQo_bxEj5TU,3385
|
90
90
|
modal/_utils/deprecation.py,sha256=dycySRBxyZf3ITzEqPNM6MxXTk9-0VVLA8oCPQ5j_Os,3426
|
91
|
-
modal/_utils/function_utils.py,sha256=
|
91
|
+
modal/_utils/function_utils.py,sha256=7vIYYezYcKA-19wBJFaoquVMLfD4LKCTlZlo92an-nY,25141
|
92
92
|
modal/_utils/grpc_testing.py,sha256=H1zHqthv19eGPJz2HKXDyWXWGSqO4BRsxah3L5Xaa8A,8619
|
93
93
|
modal/_utils/grpc_utils.py,sha256=PPB5ay-vXencXNIWPVw5modr3EH7gfq2QPcO5YJ1lMU,7737
|
94
94
|
modal/_utils/hash_utils.py,sha256=zg3J6OGxTFGSFri1qQ12giDz90lWk8bzaxCTUCRtiX4,3034
|
@@ -147,13 +147,13 @@ modal_global_objects/mounts/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0
|
|
147
147
|
modal_global_objects/mounts/modal_client_package.py,sha256=W0E_yShsRojPzWm6LtIQqNVolapdnrZkm2hVEQuZK_4,767
|
148
148
|
modal_global_objects/mounts/python_standalone.py,sha256=SL_riIxpd8mP4i4CLDCWiFFNj0Ltknm9c_UIGfX5d60,1836
|
149
149
|
modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
150
|
-
modal_proto/api.proto,sha256=
|
151
|
-
modal_proto/api_grpc.py,sha256=
|
152
|
-
modal_proto/api_pb2.py,sha256=
|
153
|
-
modal_proto/api_pb2.pyi,sha256=
|
154
|
-
modal_proto/api_pb2_grpc.py,sha256=
|
155
|
-
modal_proto/api_pb2_grpc.pyi,sha256=
|
156
|
-
modal_proto/modal_api_grpc.py,sha256
|
150
|
+
modal_proto/api.proto,sha256=LdD5XC5IhLP4rL_7nLeO0FL3emTs7jpEZGON2JIJDs4,79741
|
151
|
+
modal_proto/api_grpc.py,sha256=AiUCWTHQQFC9RFB_XuavB_OnVMr7GJMRLEwcf4FSTio,102088
|
152
|
+
modal_proto/api_pb2.py,sha256=rvBtjxpHVIXZmecc0OZS5PgZm4n1DcI4q1EHE675iW0,293583
|
153
|
+
modal_proto/api_pb2.pyi,sha256=LNvol6MFzFuG1J_wKA4AKPiSKSLkYfNW1kF7c5XXABk,391781
|
154
|
+
modal_proto/api_pb2_grpc.py,sha256=dFxVTgosyp_o8NCI1JIySlR0qTzG4ILm9mq8MNo4jYc,220795
|
155
|
+
modal_proto/api_pb2_grpc.pyi,sha256=yJgwEl-1YU42m7MU_Sm5SK3rB9xdkisPk3nZB-mlqkg,51463
|
156
|
+
modal_proto/modal_api_grpc.py,sha256=MyNzvY_WqB7QTMOQjoH6lsqCY5-6_s1HP-knsOSjANs,13640
|
157
157
|
modal_proto/modal_options_grpc.py,sha256=qJ1cuwA54oRqrdTyPTbvfhFZYd9HhJKK5UCwt523r3Y,120
|
158
158
|
modal_proto/options.proto,sha256=a-siq4swVbZPfaFRXAipRZzGP2bq8OsdUvjlyzAeodQ,488
|
159
159
|
modal_proto/options_grpc.py,sha256=M18X3d-8F_cNYSVM3I25dUTO5rZ0rd-vCCfynfh13Nc,125
|
@@ -164,10 +164,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
|
|
164
164
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
165
165
|
modal_version/__init__.py,sha256=RT6zPoOdFO99u5Wcxxaoir4ZCuPTbQ22cvzFAXl3vUY,470
|
166
166
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
167
|
-
modal_version/_version_generated.py,sha256=
|
168
|
-
modal-0.68.
|
169
|
-
modal-0.68.
|
170
|
-
modal-0.68.
|
171
|
-
modal-0.68.
|
172
|
-
modal-0.68.
|
173
|
-
modal-0.68.
|
167
|
+
modal_version/_version_generated.py,sha256=TJegGbOMTEgViX-4rKkRQVJOLfd0ljcg-Q5dRYNs7kc,149
|
168
|
+
modal-0.68.49.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
169
|
+
modal-0.68.49.dist-info/METADATA,sha256=R8kXW2CA57SO2XcJQ_rDphLvAP2kKbI0qGSbzh06Udw,2329
|
170
|
+
modal-0.68.49.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
171
|
+
modal-0.68.49.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
172
|
+
modal-0.68.49.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
|
173
|
+
modal-0.68.49.dist-info/RECORD,,
|
modal_proto/api.proto
CHANGED
@@ -321,6 +321,14 @@ message AppGetByDeploymentNameResponse {
|
|
321
321
|
string app_id = 1;
|
322
322
|
}
|
323
323
|
|
324
|
+
message AppGetLayoutRequest {
|
325
|
+
string app_id = 1;
|
326
|
+
}
|
327
|
+
|
328
|
+
message AppGetLayoutResponse {
|
329
|
+
AppLayout app_layout = 1;
|
330
|
+
}
|
331
|
+
|
324
332
|
message AppGetLogsRequest {
|
325
333
|
string app_id = 1;
|
326
334
|
float timeout = 2;
|
@@ -2700,6 +2708,7 @@ service ModalClient {
|
|
2700
2708
|
rpc AppDeploy(AppDeployRequest) returns (AppDeployResponse);
|
2701
2709
|
rpc AppDeploymentHistory(AppDeploymentHistoryRequest) returns (AppDeploymentHistoryResponse);
|
2702
2710
|
rpc AppGetByDeploymentName(AppGetByDeploymentNameRequest) returns (AppGetByDeploymentNameResponse);
|
2711
|
+
rpc AppGetLayout(AppGetLayoutRequest) returns (AppGetLayoutResponse);
|
2703
2712
|
rpc AppGetLogs(AppGetLogsRequest) returns (stream TaskLogsBatch);
|
2704
2713
|
rpc AppGetObjects(AppGetObjectsRequest) returns (AppGetObjectsResponse);
|
2705
2714
|
rpc AppGetOrCreate(AppGetOrCreateRequest) returns (AppGetOrCreateResponse);
|