modal 0.72.57__py3-none-any.whl → 0.73.0__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/container_io_manager.py +1 -1
- modal/_utils/function_utils.py +45 -16
- modal/_utils/rand_pb_testing.py +4 -4
- modal/app.py +9 -0
- modal/app.pyi +10 -0
- modal/cli/import_refs.py +1 -1
- modal/client.pyi +2 -2
- modal/file_pattern_matcher.py +0 -1
- modal/functions.py +15 -12
- modal/functions.pyi +8 -6
- modal/image.py +1 -1
- modal/sandbox.py +3 -1
- {modal-0.72.57.dist-info → modal-0.73.0.dist-info}/METADATA +1 -1
- {modal-0.72.57.dist-info → modal-0.73.0.dist-info}/RECORD +20 -20
- modal_version/__init__.py +1 -1
- modal_version/_version_generated.py +1 -1
- {modal-0.72.57.dist-info → modal-0.73.0.dist-info}/LICENSE +0 -0
- {modal-0.72.57.dist-info → modal-0.73.0.dist-info}/WHEEL +0 -0
- {modal-0.72.57.dist-info → modal-0.73.0.dist-info}/entry_points.txt +0 -0
- {modal-0.72.57.dist-info → modal-0.73.0.dist-info}/top_level.txt +0 -0
@@ -1017,7 +1017,7 @@ def check_fastapi_pydantic_compatibility(exc: ImportError) -> None:
|
|
1017
1017
|
if pydantic_version >= (2, 0) and fastapi_version < (0, 100):
|
1018
1018
|
if sys.version_info < (3, 11):
|
1019
1019
|
# https://peps.python.org/pep-0678/
|
1020
|
-
exc.__notes__ = [note]
|
1020
|
+
exc.__notes__ = [note] # type: ignore
|
1021
1021
|
else:
|
1022
1022
|
exc.add_note(note)
|
1023
1023
|
except Exception:
|
modal/_utils/function_utils.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Copyright Modal Labs 2022
|
2
2
|
import asyncio
|
3
|
+
import enum
|
3
4
|
import inspect
|
4
5
|
import os
|
5
6
|
from collections.abc import AsyncGenerator
|
@@ -319,34 +320,32 @@ class FunctionInfo:
|
|
319
320
|
These are typically local modules which are imported but not part of the running package
|
320
321
|
|
321
322
|
"""
|
322
|
-
if self.
|
323
|
-
# Don't auto-mount anything for notebooks
|
323
|
+
if self.is_serialized():
|
324
|
+
# Don't auto-mount anything for serialized functions (including notebooks)
|
324
325
|
return []
|
325
326
|
|
326
327
|
# make sure the function's own entrypoint is included:
|
327
328
|
if self._type == FunctionInfoType.PACKAGE:
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
remote_path=self._remote_dir,
|
336
|
-
recursive=True,
|
337
|
-
condition=entrypoint_only_package_mount_condition(self._file),
|
338
|
-
)
|
339
|
-
]
|
340
|
-
elif not self.is_serialized():
|
329
|
+
top_level_package = self.module_name.split(".")[0]
|
330
|
+
# TODO: add deprecation warning if the following entrypoint mount
|
331
|
+
# includes non-.py files, since we'll want to migrate to .py-only
|
332
|
+
# soon to get it consistent with the `add_local_python_source()`
|
333
|
+
# defaults.
|
334
|
+
return [_Mount._from_local_python_packages(top_level_package)]
|
335
|
+
elif self._type == FunctionInfoType.FILE:
|
341
336
|
remote_path = ROOT_DIR / Path(self._file).name
|
342
337
|
if not _is_modal_path(remote_path):
|
338
|
+
# TODO: inspect if this file is already included as part of
|
339
|
+
# a package mount, and skip it + reference that package
|
340
|
+
# instead if that's the case. This avoids possible module
|
341
|
+
# duplication bugs
|
343
342
|
return [
|
344
343
|
_Mount._from_local_file(
|
345
344
|
self._file,
|
346
345
|
remote_path=remote_path,
|
347
346
|
)
|
348
347
|
]
|
349
|
-
return []
|
348
|
+
return [] # this should never be reached...
|
350
349
|
|
351
350
|
def get_tag(self):
|
352
351
|
return self.function_name
|
@@ -622,3 +621,33 @@ class FunctionCreationStatus:
|
|
622
621
|
f"Custom domain for {method_definition.function_name} => [magenta underline]"
|
623
622
|
f"{custom_domain.url}[/magenta underline]"
|
624
623
|
)
|
624
|
+
|
625
|
+
|
626
|
+
class IncludeSourceMode(enum.Enum):
|
627
|
+
INCLUDE_NOTHING = False # can only be set in source, can't be set in config
|
628
|
+
INCLUDE_MAIN_PACKAGE = True # also represented by AUTOMOUNT=0 in config
|
629
|
+
INCLUDE_FIRST_PARTY = "legacy" # mounts all "local" modules in sys.modules - represented by AUTOMOUNT=1 in config
|
630
|
+
|
631
|
+
|
632
|
+
def get_include_source_mode(function_or_app_specific) -> IncludeSourceMode:
|
633
|
+
"""Which "automount" behavior should a function use
|
634
|
+
|
635
|
+
function_or_app_specific: explicit value given in the @function or @cls decorator, in an App constructor, or None
|
636
|
+
|
637
|
+
If function_or_app_specific is specified, validate and return the IncludeSourceMode
|
638
|
+
If function_or_app_specific is None, infer it from config
|
639
|
+
"""
|
640
|
+
if function_or_app_specific is not None:
|
641
|
+
if not isinstance(function_or_app_specific, bool):
|
642
|
+
raise ValueError(
|
643
|
+
f"Invalid `include_source` value: {function_or_app_specific}. Use one of:\n"
|
644
|
+
f"True - include function's package source\n"
|
645
|
+
f"False - include no Python source (module expected to be present in Image)\n"
|
646
|
+
)
|
647
|
+
|
648
|
+
# explicitly set in app/function
|
649
|
+
return IncludeSourceMode(function_or_app_specific)
|
650
|
+
|
651
|
+
# note that the automount config boolean isn't a 1-1 mapping with include_source!
|
652
|
+
legacy_automount_mode: bool = config.get("automount")
|
653
|
+
return IncludeSourceMode.INCLUDE_FIRST_PARTY if legacy_automount_mode else IncludeSourceMode.INCLUDE_MAIN_PACKAGE
|
modal/_utils/rand_pb_testing.py
CHANGED
@@ -7,7 +7,7 @@ Modal, with random seeds, and it supports oneofs, and Protobuf v4.
|
|
7
7
|
|
8
8
|
import string
|
9
9
|
from random import Random
|
10
|
-
from typing import Any, Callable, Optional, TypeVar
|
10
|
+
from typing import Any, Callable, Optional, TypeVar, Union
|
11
11
|
|
12
12
|
from google.protobuf.descriptor import Descriptor, FieldDescriptor
|
13
13
|
|
@@ -38,9 +38,9 @@ def _fill(msg, desc: Descriptor, rand: Random) -> None:
|
|
38
38
|
field: FieldDescriptor
|
39
39
|
oneof_fields: set[str] = set()
|
40
40
|
for oneof in desc.oneofs:
|
41
|
-
|
42
|
-
if
|
43
|
-
oneof_fields.add(
|
41
|
+
oneof_field: Union[FieldDescriptor, None] = rand.choice(list(oneof.fields) + [None])
|
42
|
+
if oneof_field is not None:
|
43
|
+
oneof_fields.add(oneof_field.name)
|
44
44
|
for field in desc.fields:
|
45
45
|
if field.containing_oneof is not None and field.name not in oneof_fields:
|
46
46
|
continue
|
modal/app.py
CHANGED
@@ -170,6 +170,8 @@ class _App:
|
|
170
170
|
_running_app: Optional[RunningApp] # Various app info
|
171
171
|
_client: Optional[_Client]
|
172
172
|
|
173
|
+
_include_source_default: Optional[bool] = None
|
174
|
+
|
173
175
|
def __init__(
|
174
176
|
self,
|
175
177
|
name: Optional[str] = None,
|
@@ -178,6 +180,7 @@ class _App:
|
|
178
180
|
mounts: Sequence[_Mount] = [], # default mounts for all functions
|
179
181
|
secrets: Sequence[_Secret] = [], # default secrets for all functions
|
180
182
|
volumes: dict[Union[str, PurePosixPath], _Volume] = {}, # default volumes for all functions
|
183
|
+
include_source: Optional[bool] = None,
|
181
184
|
) -> None:
|
182
185
|
"""Construct a new app, optionally with default image, mounts, secrets, or volumes.
|
183
186
|
|
@@ -193,6 +196,7 @@ class _App:
|
|
193
196
|
|
194
197
|
self._name = name
|
195
198
|
self._description = name
|
199
|
+
self._include_source_default = include_source
|
196
200
|
|
197
201
|
check_sequence(mounts, _Mount, "`mounts=` has to be a list or tuple of `modal.Mount` objects")
|
198
202
|
check_sequence(secrets, _Secret, "`secrets=` has to be a list or tuple of `modal.Secret` objects")
|
@@ -603,6 +607,8 @@ class _App:
|
|
603
607
|
# With `max_inputs = 1`, containers will be single-use.
|
604
608
|
max_inputs: Optional[int] = None,
|
605
609
|
i6pn: Optional[bool] = None, # Whether to enable IPv6 container networking within the region.
|
610
|
+
# Whether the function's home package should be included in the image - defaults to True
|
611
|
+
include_source: Optional[bool] = None,
|
606
612
|
# Parameters below here are experimental. Use with caution!
|
607
613
|
_experimental_scheduler_placement: Optional[
|
608
614
|
SchedulerPlacement
|
@@ -751,6 +757,7 @@ class _App:
|
|
751
757
|
_experimental_proxy_ip=_experimental_proxy_ip,
|
752
758
|
i6pn_enabled=i6pn_enabled,
|
753
759
|
cluster_size=cluster_size, # Experimental: Clustered functions
|
760
|
+
include_source=include_source if include_source is not None else self._include_source_default,
|
754
761
|
)
|
755
762
|
|
756
763
|
self._add_function(function, webhook_config is not None)
|
@@ -800,6 +807,7 @@ class _App:
|
|
800
807
|
# Limits the number of inputs a container handles before shutting down.
|
801
808
|
# Use `max_inputs = 1` for single-use containers.
|
802
809
|
max_inputs: Optional[int] = None,
|
810
|
+
include_source: Optional[bool] = None,
|
803
811
|
# Parameters below here are experimental. Use with caution!
|
804
812
|
_experimental_scheduler_placement: Optional[
|
805
813
|
SchedulerPlacement
|
@@ -877,6 +885,7 @@ class _App:
|
|
877
885
|
block_network=block_network,
|
878
886
|
max_inputs=max_inputs,
|
879
887
|
scheduler_placement=scheduler_placement,
|
888
|
+
include_source=include_source if include_source is not None else self._include_source_default,
|
880
889
|
_experimental_buffer_containers=_experimental_buffer_containers,
|
881
890
|
_experimental_proxy_ip=_experimental_proxy_ip,
|
882
891
|
_experimental_custom_scaling_factor=_experimental_custom_scaling_factor,
|
modal/app.pyi
CHANGED
@@ -90,6 +90,7 @@ class _App:
|
|
90
90
|
_app_id: typing.Optional[str]
|
91
91
|
_running_app: typing.Optional[modal.running_app.RunningApp]
|
92
92
|
_client: typing.Optional[modal.client._Client]
|
93
|
+
_include_source_default: typing.Optional[bool]
|
93
94
|
|
94
95
|
def __init__(
|
95
96
|
self,
|
@@ -99,6 +100,7 @@ class _App:
|
|
99
100
|
mounts: collections.abc.Sequence[modal.mount._Mount] = [],
|
100
101
|
secrets: collections.abc.Sequence[modal.secret._Secret] = [],
|
101
102
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume] = {},
|
103
|
+
include_source: typing.Optional[bool] = None,
|
102
104
|
) -> None: ...
|
103
105
|
@property
|
104
106
|
def name(self) -> typing.Optional[str]: ...
|
@@ -189,6 +191,7 @@ class _App:
|
|
189
191
|
block_network: bool = False,
|
190
192
|
max_inputs: typing.Optional[int] = None,
|
191
193
|
i6pn: typing.Optional[bool] = None,
|
194
|
+
include_source: typing.Optional[bool] = None,
|
192
195
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
193
196
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
194
197
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
@@ -232,6 +235,7 @@ class _App:
|
|
232
235
|
enable_memory_snapshot: bool = False,
|
233
236
|
block_network: bool = False,
|
234
237
|
max_inputs: typing.Optional[int] = None,
|
238
|
+
include_source: typing.Optional[bool] = None,
|
235
239
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
236
240
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
237
241
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
@@ -288,6 +292,7 @@ class App:
|
|
288
292
|
_app_id: typing.Optional[str]
|
289
293
|
_running_app: typing.Optional[modal.running_app.RunningApp]
|
290
294
|
_client: typing.Optional[modal.client.Client]
|
295
|
+
_include_source_default: typing.Optional[bool]
|
291
296
|
|
292
297
|
def __init__(
|
293
298
|
self,
|
@@ -297,6 +302,7 @@ class App:
|
|
297
302
|
mounts: collections.abc.Sequence[modal.mount.Mount] = [],
|
298
303
|
secrets: collections.abc.Sequence[modal.secret.Secret] = [],
|
299
304
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume] = {},
|
305
|
+
include_source: typing.Optional[bool] = None,
|
300
306
|
) -> None: ...
|
301
307
|
@property
|
302
308
|
def name(self) -> typing.Optional[str]: ...
|
@@ -419,6 +425,7 @@ class App:
|
|
419
425
|
block_network: bool = False,
|
420
426
|
max_inputs: typing.Optional[int] = None,
|
421
427
|
i6pn: typing.Optional[bool] = None,
|
428
|
+
include_source: typing.Optional[bool] = None,
|
422
429
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
423
430
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
424
431
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
@@ -462,6 +469,7 @@ class App:
|
|
462
469
|
enable_memory_snapshot: bool = False,
|
463
470
|
block_network: bool = False,
|
464
471
|
max_inputs: typing.Optional[int] = None,
|
472
|
+
include_source: typing.Optional[bool] = None,
|
465
473
|
_experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
|
466
474
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
467
475
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
@@ -547,6 +555,7 @@ class _Stub(_App):
|
|
547
555
|
mounts: collections.abc.Sequence[modal.mount._Mount] = [],
|
548
556
|
secrets: collections.abc.Sequence[modal.secret._Secret] = [],
|
549
557
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume] = {},
|
558
|
+
include_source: typing.Optional[bool] = None,
|
550
559
|
): ...
|
551
560
|
|
552
561
|
class Stub(App):
|
@@ -558,6 +567,7 @@ class Stub(App):
|
|
558
567
|
mounts: collections.abc.Sequence[modal.mount.Mount] = [],
|
559
568
|
secrets: collections.abc.Sequence[modal.secret.Secret] = [],
|
560
569
|
volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume] = {},
|
570
|
+
include_source: typing.Optional[bool] = None,
|
561
571
|
) -> None: ...
|
562
572
|
|
563
573
|
_default_image: modal.image._Image
|
modal/cli/import_refs.py
CHANGED
@@ -226,7 +226,7 @@ def import_app(app_ref: str) -> App:
|
|
226
226
|
error_console = Console(stderr=True)
|
227
227
|
error_console.print(f"[bold red]Could not find Modal app '{object_path}' in {import_path}.[/bold red]")
|
228
228
|
|
229
|
-
if object_path
|
229
|
+
if not object_path:
|
230
230
|
guidance_msg = Markdown(
|
231
231
|
f"Expected to find an app variable named **`{DEFAULT_APP_NAME}`** (the default app name). "
|
232
232
|
"If your `modal.App` is assigned to a different variable name, "
|
modal/client.pyi
CHANGED
@@ -27,7 +27,7 @@ class _Client:
|
|
27
27
|
_snapshotted: bool
|
28
28
|
|
29
29
|
def __init__(
|
30
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.
|
30
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.0"
|
31
31
|
): ...
|
32
32
|
def is_closed(self) -> bool: ...
|
33
33
|
@property
|
@@ -85,7 +85,7 @@ class Client:
|
|
85
85
|
_snapshotted: bool
|
86
86
|
|
87
87
|
def __init__(
|
88
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.
|
88
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.0"
|
89
89
|
): ...
|
90
90
|
def is_closed(self) -> bool: ...
|
91
91
|
@property
|
modal/file_pattern_matcher.py
CHANGED
@@ -144,7 +144,6 @@ class FilePatternMatcher(_AbstractPatternMatcher):
|
|
144
144
|
library. The reason is that `Matches()` in the original library is
|
145
145
|
deprecated due to buggy behavior.
|
146
146
|
"""
|
147
|
-
|
148
147
|
matched = False
|
149
148
|
file_path = os.path.normpath(file_path)
|
150
149
|
if file_path == ".":
|
modal/functions.py
CHANGED
@@ -8,13 +8,7 @@ import warnings
|
|
8
8
|
from collections.abc import AsyncGenerator, Collection, Sequence, Sized
|
9
9
|
from dataclasses import dataclass
|
10
10
|
from pathlib import PurePosixPath
|
11
|
-
from typing import
|
12
|
-
TYPE_CHECKING,
|
13
|
-
Any,
|
14
|
-
Callable,
|
15
|
-
Optional,
|
16
|
-
Union,
|
17
|
-
)
|
11
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
18
12
|
|
19
13
|
import typing_extensions
|
20
14
|
from google.protobuf.message import Message
|
@@ -48,10 +42,12 @@ from ._utils.function_utils import (
|
|
48
42
|
OUTPUTS_TIMEOUT,
|
49
43
|
FunctionCreationStatus,
|
50
44
|
FunctionInfo,
|
45
|
+
IncludeSourceMode,
|
51
46
|
_create_input,
|
52
47
|
_process_result,
|
53
48
|
_stream_function_call_data,
|
54
49
|
get_function_type,
|
50
|
+
get_include_source_mode,
|
55
51
|
is_async,
|
56
52
|
)
|
57
53
|
from ._utils.grpc_utils import retry_transient_errors
|
@@ -474,6 +470,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
474
470
|
cluster_size: Optional[int] = None, # Experimental: Clustered functions
|
475
471
|
max_inputs: Optional[int] = None,
|
476
472
|
ephemeral_disk: Optional[int] = None,
|
473
|
+
# current default: first-party, future default: main-package
|
474
|
+
include_source: Optional[bool] = None,
|
477
475
|
_experimental_buffer_containers: Optional[int] = None,
|
478
476
|
_experimental_proxy_ip: Optional[str] = None,
|
479
477
|
_experimental_custom_scaling_factor: Optional[float] = None,
|
@@ -500,7 +498,11 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
500
498
|
explicit_mounts = mounts
|
501
499
|
|
502
500
|
if is_local():
|
503
|
-
|
501
|
+
include_source_mode = get_include_source_mode(include_source)
|
502
|
+
if include_source_mode != IncludeSourceMode.INCLUDE_NOTHING:
|
503
|
+
entrypoint_mounts = info.get_entrypoint_mount()
|
504
|
+
else:
|
505
|
+
entrypoint_mounts = []
|
504
506
|
|
505
507
|
all_mounts = [
|
506
508
|
_get_client_mount(),
|
@@ -508,12 +510,15 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
508
510
|
*entrypoint_mounts,
|
509
511
|
]
|
510
512
|
|
511
|
-
if
|
513
|
+
if include_source_mode is IncludeSourceMode.INCLUDE_FIRST_PARTY:
|
514
|
+
# TODO(elias): if using INCLUDE_FIRST_PARTY *and* mounts are added that haven't already been
|
515
|
+
# added to the image via add_local_python_source
|
512
516
|
all_mounts += get_auto_mounts()
|
513
517
|
else:
|
514
518
|
# skip any mount introspection/logic inside containers, since the function
|
515
519
|
# should already be hydrated
|
516
|
-
# TODO: maybe the entire
|
520
|
+
# TODO: maybe the entire from_args loader should be exited early if not local?
|
521
|
+
# since it will be hydrated
|
517
522
|
all_mounts = []
|
518
523
|
|
519
524
|
retry_policy = _parse_retries(
|
@@ -951,8 +956,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
951
956
|
parent = self
|
952
957
|
|
953
958
|
async def _load(param_bound_func: _Function, resolver: Resolver, existing_object_id: Optional[str]):
|
954
|
-
if parent is None:
|
955
|
-
raise ExecutionError("Can't find the parent class' service function")
|
956
959
|
try:
|
957
960
|
identity = f"{parent.info.function_name} class service function"
|
958
961
|
except Exception:
|
modal/functions.pyi
CHANGED
@@ -198,6 +198,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal._object
|
|
198
198
|
cluster_size: typing.Optional[int] = None,
|
199
199
|
max_inputs: typing.Optional[int] = None,
|
200
200
|
ephemeral_disk: typing.Optional[int] = None,
|
201
|
+
include_source: typing.Optional[bool] = None,
|
201
202
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
202
203
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
203
204
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
@@ -369,6 +370,7 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
369
370
|
cluster_size: typing.Optional[int] = None,
|
370
371
|
max_inputs: typing.Optional[int] = None,
|
371
372
|
ephemeral_disk: typing.Optional[int] = None,
|
373
|
+
include_source: typing.Optional[bool] = None,
|
372
374
|
_experimental_buffer_containers: typing.Optional[int] = None,
|
373
375
|
_experimental_proxy_ip: typing.Optional[str] = None,
|
374
376
|
_experimental_custom_scaling_factor: typing.Optional[float] = None,
|
@@ -469,11 +471,11 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
469
471
|
|
470
472
|
_call_generator_nowait: ___call_generator_nowait_spec[typing_extensions.Self]
|
471
473
|
|
472
|
-
class __remote_spec(typing_extensions.Protocol[
|
474
|
+
class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
|
473
475
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
474
476
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
475
477
|
|
476
|
-
remote: __remote_spec[
|
478
|
+
remote: __remote_spec[P, ReturnType, typing_extensions.Self]
|
477
479
|
|
478
480
|
class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
|
479
481
|
def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
|
@@ -486,17 +488,17 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
486
488
|
def _get_obj(self) -> typing.Optional[modal.cls.Obj]: ...
|
487
489
|
def local(self, *args: P.args, **kwargs: P.kwargs) -> OriginalReturnType: ...
|
488
490
|
|
489
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
491
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
|
490
492
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
491
493
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
492
494
|
|
493
|
-
_experimental_spawn: ___experimental_spawn_spec[
|
495
|
+
_experimental_spawn: ___experimental_spawn_spec[P, ReturnType, typing_extensions.Self]
|
494
496
|
|
495
|
-
class __spawn_spec(typing_extensions.Protocol[
|
497
|
+
class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
|
496
498
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
497
499
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
498
500
|
|
499
|
-
spawn: __spawn_spec[
|
501
|
+
spawn: __spawn_spec[P, ReturnType, typing_extensions.Self]
|
500
502
|
|
501
503
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
|
502
504
|
|
modal/image.py
CHANGED
@@ -2054,7 +2054,7 @@ class _Image(_Object, type_prefix="im"):
|
|
2054
2054
|
yield
|
2055
2055
|
except Exception as exc:
|
2056
2056
|
if not self.is_hydrated:
|
2057
|
-
# Might be hydrated later
|
2057
|
+
# Might be hydrated later (if it's the container's used image)
|
2058
2058
|
self.inside_exceptions.append(exc)
|
2059
2059
|
elif env_image_id == self.object_id:
|
2060
2060
|
# Image is already hydrated (we can remove this case later
|
modal/sandbox.py
CHANGED
@@ -581,7 +581,9 @@ class _Sandbox(_Object, type_prefix="sb"):
|
|
581
581
|
)
|
582
582
|
sandbox = await _Sandbox.from_id(restore_resp.sandbox_id, client)
|
583
583
|
|
584
|
-
task_id_req = api_pb2.SandboxGetTaskIdRequest(
|
584
|
+
task_id_req = api_pb2.SandboxGetTaskIdRequest(
|
585
|
+
sandbox_id=restore_resp.sandbox_id, wait_until_ready=True, timeout=55.0
|
586
|
+
)
|
585
587
|
resp = await retry_transient_errors(client.stub.SandboxGetTaskId, task_id_req)
|
586
588
|
if resp.task_result.status not in [
|
587
589
|
api_pb2.GenericResult.GENERIC_STATUS_UNSPECIFIED,
|
@@ -16,11 +16,11 @@ modal/_traceback.py,sha256=IZQzB3fVlUfMHOSyKUgw0H6qv4yHnpyq-XVCNZKfUdA,5023
|
|
16
16
|
modal/_tunnel.py,sha256=zTBxBiuH1O22tS1OliAJdIsSmaZS8PlnifS_6S5z-mk,6320
|
17
17
|
modal/_tunnel.pyi,sha256=JmmDYAy9F1FpgJ_hWx0xkom2nTOFQjn4mTPYlU3PFo4,1245
|
18
18
|
modal/_watcher.py,sha256=K6LYnlmSGQB4tWWI9JADv-tvSvQ1j522FwT71B51CX8,3584
|
19
|
-
modal/app.py,sha256=
|
20
|
-
modal/app.pyi,sha256=
|
19
|
+
modal/app.py,sha256=BEl90AuVlcrVlXKhM0wDFn1E8hewvbia9Qdq6SiT-FE,44225
|
20
|
+
modal/app.pyi,sha256=NnUpnKq5y5fM45F5cXLMZK5zVJZA9T_UKawloOXCVXE,25858
|
21
21
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
22
22
|
modal/client.py,sha256=8SQawr7P1PNUCq1UmJMUQXG2jIo4Nmdcs311XqrNLRE,15276
|
23
|
-
modal/client.pyi,sha256=
|
23
|
+
modal/client.pyi,sha256=zrgKN8G3exCslhD6GQkTjD8tddibnNtFRMx6ZiHnyhU,7591
|
24
24
|
modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
|
25
25
|
modal/cloud_bucket_mount.pyi,sha256=30T3K1a89l6wzmEJ_J9iWv9SknoGqaZDx59Xs-ZQcmk,1607
|
26
26
|
modal/cls.py,sha256=7k3_FwhPUzewRXRZP8bcVJA9AZstoxGJuHKQ5Db-YoY,32683
|
@@ -37,11 +37,11 @@ modal/experimental.py,sha256=H9FXT3kshbjPLovshT10DicyOkGFrPqILy-qdTX-P7s,4015
|
|
37
37
|
modal/experimental.pyi,sha256=24tIYu_w9RLwFrz1cIsgYuqmDCtV8eg6-bQNz3zjhDo,939
|
38
38
|
modal/file_io.py,sha256=lcMs_E9Xfm0YX1t9U2wNIBPnqHRxmImqjLW1GHqVmyg,20945
|
39
39
|
modal/file_io.pyi,sha256=NTRft1tbPSWf9TlWVeZmTlgB5AZ_Zhu2srWIrWr7brk,9445
|
40
|
-
modal/file_pattern_matcher.py,sha256=
|
41
|
-
modal/functions.py,sha256=
|
42
|
-
modal/functions.pyi,sha256=
|
40
|
+
modal/file_pattern_matcher.py,sha256=1cZ4V2wSLiaXqAqStETSwp3bzDD6QZOt6pmmjk3Okz4,6505
|
41
|
+
modal/functions.py,sha256=bD4NbWgAfo5jsiqlT5XT0nGTaQ_6zOfyNmwYu0I-aVY,69951
|
42
|
+
modal/functions.pyi,sha256=j6FWZzwFVSaxyOy4zcmCwz2wlAhWeuzqsjndNKt7VlA,26641
|
43
43
|
modal/gpu.py,sha256=2qZMNnoMrjU-5Bu7fx68pANUAKTtZq0EWEEeBA9OUVQ,7426
|
44
|
-
modal/image.py,sha256=
|
44
|
+
modal/image.py,sha256=c6-RsdVDCscmfOoZI26gj8GvBBw1oVC18n8WaYKBWAw,90949
|
45
45
|
modal/image.pyi,sha256=QMKS6E3CsZr5DoyNqGpcJPBYJTJZSvtAQIsAhPVd_E4,26347
|
46
46
|
modal/io_streams.py,sha256=QkQiizKRzd5bnbKQsap31LJgBYlAnj4-XkV_50xPYX0,15079
|
47
47
|
modal/io_streams.pyi,sha256=bJ7ZLmSmJ0nKoa6r4FJpbqvzdUVa0lEe0Fa-MMpMezU,5071
|
@@ -65,7 +65,7 @@ modal/retries.py,sha256=HKR2Q9aNPWkMjQ5nwobqYTuZaSuw0a8lI2zrtY5IW98,5230
|
|
65
65
|
modal/runner.py,sha256=0SCMgKO8lZ9W1C7le1EcgViKERMXpi_-QBd6PF_MH0Q,24450
|
66
66
|
modal/runner.pyi,sha256=YmP4EOCNjjkwSIPi2Gl6hF_ji_ytkxz9dw3iB9KXaOI,5275
|
67
67
|
modal/running_app.py,sha256=v61mapYNV1-O-Uaho5EfJlryMLvIT9We0amUOSvSGx8,1188
|
68
|
-
modal/sandbox.py,sha256=
|
68
|
+
modal/sandbox.py,sha256=fMvBs3KZV08kT9jo8roT7yNS78ENGef2xHIAuuXeuK4,31809
|
69
69
|
modal/sandbox.pyi,sha256=qncEvzK76h_ehrs03vlroQyLThWiMsjKhD0DnCNc6zI,22663
|
70
70
|
modal/schedule.py,sha256=0ZFpKs1bOxeo5n3HZjoL7OE2ktsb-_oGtq-WJEPO4tY,2615
|
71
71
|
modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
|
@@ -82,7 +82,7 @@ modal/volume.py,sha256=JAWeDvoAG95tMBv-fYIERyHsJPS_X_xGpxRRmYtb6j0,30096
|
|
82
82
|
modal/volume.pyi,sha256=kTsXarphjZILXci84LQy7EyC84eXUs5-7D62IM5q3eE,12491
|
83
83
|
modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
84
84
|
modal/_runtime/asgi.py,sha256=c4hmaMW1pLo-cm7ouriJjieuFm4ZF6D2LMy0638sfOs,22139
|
85
|
-
modal/_runtime/container_io_manager.py,sha256=
|
85
|
+
modal/_runtime/container_io_manager.py,sha256=2WmFnErVZyfyw3P6iiv1N6FskxZGsiUHrJe7nJ_-iTs,43161
|
86
86
|
modal/_runtime/execution_context.py,sha256=E6ofm6j1POXGPxS841X3V7JU6NheVb8OkQc7JpLq4Kg,2712
|
87
87
|
modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5163
|
88
88
|
modal/_runtime/user_code_imports.py,sha256=c06azC0MtYHHCGWWRGxsVY0uMAjQKe1QEj-i0oXqbWM,14699
|
@@ -93,7 +93,7 @@ modal/_utils/blob_utils.py,sha256=N66LtZI8PpCkZ7maA7GLW5CAmYUoNJdG-GjaAUR4_NQ,14
|
|
93
93
|
modal/_utils/bytes_io_segment_payload.py,sha256=uunxVJS4PE1LojF_UpURMzVK9GuvmYWRqQo_bxEj5TU,3385
|
94
94
|
modal/_utils/deprecation.py,sha256=dycySRBxyZf3ITzEqPNM6MxXTk9-0VVLA8oCPQ5j_Os,3426
|
95
95
|
modal/_utils/docker_utils.py,sha256=h1uETghR40mp_y3fSWuZAfbIASH1HMzuphJHghAL6DU,3722
|
96
|
-
modal/_utils/function_utils.py,sha256=
|
96
|
+
modal/_utils/function_utils.py,sha256=QAQQXPhSZVPkGgbgStFp3lDCB0jnvlcwKki1N3tSSWQ,27187
|
97
97
|
modal/_utils/grpc_testing.py,sha256=H1zHqthv19eGPJz2HKXDyWXWGSqO4BRsxah3L5Xaa8A,8619
|
98
98
|
modal/_utils/grpc_utils.py,sha256=PPB5ay-vXencXNIWPVw5modr3EH7gfq2QPcO5YJ1lMU,7737
|
99
99
|
modal/_utils/hash_utils.py,sha256=zg3J6OGxTFGSFri1qQ12giDz90lWk8bzaxCTUCRtiX4,3034
|
@@ -103,7 +103,7 @@ modal/_utils/mount_utils.py,sha256=J-FRZbPQv1i__Tob-FIpbB1oXWpFLAwZiB4OCiJpFG0,3
|
|
103
103
|
modal/_utils/name_utils.py,sha256=TW1iyJedvDNPEJ5UVp93u8xuD5J2gQL_CUt1mgov_aI,1939
|
104
104
|
modal/_utils/package_utils.py,sha256=LcL2olGN4xaUzu2Tbv-C-Ft9Qp6bsLxEfETOAVd-mjU,2073
|
105
105
|
modal/_utils/pattern_utils.py,sha256=ZUffaECfe2iYBhH6cvCB-0-UWhmEBTZEl_TwG_So3ag,6714
|
106
|
-
modal/_utils/rand_pb_testing.py,sha256=
|
106
|
+
modal/_utils/rand_pb_testing.py,sha256=mmVPk1rZldHwHZx0DnHTuHQlRLAiiAYdxjwEJpxvT9c,3900
|
107
107
|
modal/_utils/shell_utils.py,sha256=hWHzv730Br2Xyj6cGPiMZ-198Z3RZuOu3pDXhFSZ22c,2157
|
108
108
|
modal/_vendor/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
109
109
|
modal/_vendor/a2wsgi_wsgi.py,sha256=Q1AsjpV_Q_vzQsz_cSqmP9jWzsGsB-ARFU6vpQYml8k,21878
|
@@ -118,7 +118,7 @@ modal/cli/container.py,sha256=FYwEgjf93j4NMorAjGbSV98i1wpebqdAeNU1wfrFp1k,3668
|
|
118
118
|
modal/cli/dict.py,sha256=8Wq3w-UDaywk8EVNdj-ECCNV9TYHqh4kzhUqhhulatM,4593
|
119
119
|
modal/cli/entry_point.py,sha256=aaNxFAqZcmtSjwzkYIA_Ba9CkL4cL4_i2gy5VjoXxkM,4228
|
120
120
|
modal/cli/environment.py,sha256=Ayddkiq9jdj3XYDJ8ZmUqFpPPH8xajYlbexRkzGtUcg,4334
|
121
|
-
modal/cli/import_refs.py,sha256
|
121
|
+
modal/cli/import_refs.py,sha256=T10EjBn-7gP-Nv5jTZyd3fEAV7zCaaLtW8eJ4Zt80Mk,11865
|
122
122
|
modal/cli/launch.py,sha256=pzQt2QlcrbIUU0MVzWWPAvMQ6MCyqsHZ0X9JcV-sY04,3242
|
123
123
|
modal/cli/network_file_system.py,sha256=eq3JnwjbfFNsJodIyANHL06ByYc3BSavzdmu8C96cHA,7948
|
124
124
|
modal/cli/profile.py,sha256=rLXfjJObfPNjaZvNfHGIKqs7y9bGYyGe-K7V0w-Ni0M,3110
|
@@ -167,12 +167,12 @@ modal_proto/options_pb2.pyi,sha256=l7DBrbLO7q3Ir-XDkWsajm0d0TQqqrfuX54i4BMpdQg,1
|
|
167
167
|
modal_proto/options_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
168
168
|
modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0yJSI,247
|
169
169
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
170
|
-
modal_version/__init__.py,sha256=
|
170
|
+
modal_version/__init__.py,sha256=wiJQ53c-OMs0Xf1UeXOxQ7FwlV1VzIjnX6o-pRYZ_Pk,470
|
171
171
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
172
|
-
modal_version/_version_generated.py,sha256=
|
173
|
-
modal-0.
|
174
|
-
modal-0.
|
175
|
-
modal-0.
|
176
|
-
modal-0.
|
177
|
-
modal-0.
|
178
|
-
modal-0.
|
172
|
+
modal_version/_version_generated.py,sha256=Yxiko82vGjUn9KsvIwjuM1GwRICvG_28r-6MWuHV0-Q,148
|
173
|
+
modal-0.73.0.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
174
|
+
modal-0.73.0.dist-info/METADATA,sha256=TmKeZvOba2fFzlrg4VGPHQ7nr4tLVPOB_SSmTLxWjsE,2328
|
175
|
+
modal-0.73.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
176
|
+
modal-0.73.0.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
177
|
+
modal-0.73.0.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
|
178
|
+
modal-0.73.0.dist-info/RECORD,,
|
modal_version/__init__.py
CHANGED
@@ -7,7 +7,7 @@ from ._version_generated import build_number
|
|
7
7
|
major_number = 0
|
8
8
|
|
9
9
|
# Bump this manually on breaking changes, then reset the number in _version_generated.py
|
10
|
-
minor_number =
|
10
|
+
minor_number = 73
|
11
11
|
|
12
12
|
# Right now, automatically increment the patch number in CI
|
13
13
|
__version__ = f"{major_number}.{minor_number}.{max(build_number, 0)}"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|