modal 0.77.0__py3-none-any.whl → 1.0.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.
Potentially problematic release.
This version of modal might be problematic. Click here for more details.
- modal/__init__.py +10 -4
- modal/_functions.py +15 -90
- modal/_object.py +0 -14
- modal/_partial_function.py +4 -14
- modal/_serialization.py +2 -2
- modal/_utils/function_utils.py +3 -6
- modal/_utils/grpc_utils.py +6 -1
- modal/app.py +1 -104
- modal/app.pyi +0 -127
- modal/cli/app.py +0 -19
- modal/cli/programs/run_jupyter.py +1 -1
- modal/cli/programs/vscode.py +1 -1
- modal/client.pyi +2 -2
- modal/cls.py +9 -14
- modal/cls.pyi +2 -17
- modal/config.py +5 -16
- modal/container_process.py +1 -9
- modal/container_process.pyi +3 -3
- modal/dict.py +3 -5
- modal/environments.py +1 -3
- modal/exception.py +1 -1
- modal/functions.pyi +8 -29
- modal/image.py +12 -36
- modal/image.pyi +2 -5
- modal/mount.py +2 -65
- modal/mount.pyi +0 -1
- modal/network_file_system.py +3 -5
- modal/object.pyi +0 -6
- modal/queue.py +3 -5
- modal/runner.py +2 -19
- modal/runner.pyi +0 -5
- modal/sandbox.py +78 -32
- modal/sandbox.pyi +102 -7
- modal/secret.py +1 -3
- modal/serving.py +0 -6
- modal/serving.pyi +0 -3
- modal/volume.py +8 -17
- {modal-0.77.0.dist-info → modal-1.0.0.dist-info}/METADATA +1 -1
- {modal-0.77.0.dist-info → modal-1.0.0.dist-info}/RECORD +51 -51
- modal_proto/api.proto +29 -1
- modal_proto/api_grpc.py +32 -0
- modal_proto/api_pb2.py +788 -756
- modal_proto/api_pb2.pyi +86 -9
- modal_proto/api_pb2_grpc.py +66 -0
- modal_proto/api_pb2_grpc.pyi +20 -0
- modal_proto/modal_api_grpc.py +2 -0
- modal_version/__init__.py +1 -1
- {modal-0.77.0.dist-info → modal-1.0.0.dist-info}/WHEEL +0 -0
- {modal-0.77.0.dist-info → modal-1.0.0.dist-info}/entry_points.txt +0 -0
- {modal-0.77.0.dist-info → modal-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {modal-0.77.0.dist-info → modal-1.0.0.dist-info}/top_level.txt +0 -0
modal/__init__.py
CHANGED
@@ -11,7 +11,7 @@ from modal_version import __version__
|
|
11
11
|
try:
|
12
12
|
from ._runtime.execution_context import current_function_call_id, current_input_id, interact, is_local
|
13
13
|
from ._tunnel import Tunnel, forward
|
14
|
-
from .app import App
|
14
|
+
from .app import App
|
15
15
|
from .client import Client
|
16
16
|
from .cloud_bucket_mount import CloudBucketMount
|
17
17
|
from .cls import Cls, parameter
|
@@ -20,7 +20,6 @@ try:
|
|
20
20
|
from .file_pattern_matcher import FilePatternMatcher
|
21
21
|
from .functions import Function, FunctionCall
|
22
22
|
from .image import Image
|
23
|
-
from .mount import Mount
|
24
23
|
from .network_file_system import NetworkFileSystem
|
25
24
|
from .output import enable_output
|
26
25
|
from .partial_function import (
|
@@ -54,6 +53,7 @@ except Exception:
|
|
54
53
|
print()
|
55
54
|
raise
|
56
55
|
|
56
|
+
|
57
57
|
__all__ = [
|
58
58
|
"__version__",
|
59
59
|
"App",
|
@@ -66,7 +66,6 @@ __all__ = [
|
|
66
66
|
"Function",
|
67
67
|
"FunctionCall",
|
68
68
|
"Image",
|
69
|
-
"Mount",
|
70
69
|
"NetworkFileSystem",
|
71
70
|
"Period",
|
72
71
|
"Proxy",
|
@@ -77,7 +76,6 @@ __all__ = [
|
|
77
76
|
"SandboxSnapshot",
|
78
77
|
"SchedulerPlacement",
|
79
78
|
"Secret",
|
80
|
-
"Stub",
|
81
79
|
"Tunnel",
|
82
80
|
"Volume",
|
83
81
|
"asgi_app",
|
@@ -99,3 +97,11 @@ __all__ = [
|
|
99
97
|
"web_server",
|
100
98
|
"wsgi_app",
|
101
99
|
]
|
100
|
+
|
101
|
+
|
102
|
+
def __getattr__(name):
|
103
|
+
if name == "Stub":
|
104
|
+
raise AttributeError(
|
105
|
+
"Module 'modal' has no attribute 'Stub'. Use `modal.App` instead. This is a simple name change."
|
106
|
+
)
|
107
|
+
raise AttributeError(f"module 'modal' has no attribute '{name}'")
|
modal/_functions.py
CHANGED
@@ -6,7 +6,7 @@ import textwrap
|
|
6
6
|
import time
|
7
7
|
import typing
|
8
8
|
import warnings
|
9
|
-
from collections.abc import AsyncGenerator,
|
9
|
+
from collections.abc import AsyncGenerator, Sequence, Sized
|
10
10
|
from dataclasses import dataclass
|
11
11
|
from pathlib import PurePosixPath
|
12
12
|
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
@@ -41,7 +41,7 @@ from ._utils.async_utils import (
|
|
41
41
|
synchronizer,
|
42
42
|
warn_if_generator_is_not_consumed,
|
43
43
|
)
|
44
|
-
from ._utils.deprecation import
|
44
|
+
from ._utils.deprecation import deprecation_warning
|
45
45
|
from ._utils.function_utils import (
|
46
46
|
ATTEMPT_TIMEOUT_GRACE_PERIOD,
|
47
47
|
OUTPUTS_TIMEOUT,
|
@@ -71,7 +71,7 @@ from .exception import (
|
|
71
71
|
)
|
72
72
|
from .gpu import GPU_T, parse_gpu_config
|
73
73
|
from .image import _Image
|
74
|
-
from .mount import _get_client_mount, _Mount
|
74
|
+
from .mount import _get_client_mount, _Mount
|
75
75
|
from .network_file_system import _NetworkFileSystem, network_file_system_mount_protos
|
76
76
|
from .output import _get_output_manager
|
77
77
|
from .parallel_map import (
|
@@ -405,12 +405,6 @@ class FunctionStats:
|
|
405
405
|
backlog: int
|
406
406
|
num_total_runners: int
|
407
407
|
|
408
|
-
def __getattr__(self, name):
|
409
|
-
if name == "num_active_runners":
|
410
|
-
msg = "'FunctionStats.num_active_runners' is no longer available."
|
411
|
-
deprecation_error((2024, 6, 14), msg)
|
412
|
-
raise AttributeError(f"'FunctionStats' object has no attribute '{name}'")
|
413
|
-
|
414
408
|
|
415
409
|
def _parse_retries(
|
416
410
|
retries: Optional[Union[int, Retries]],
|
@@ -511,7 +505,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
511
505
|
is_generator: bool = False,
|
512
506
|
gpu: Union[GPU_T, list[GPU_T]] = None,
|
513
507
|
# TODO: maybe break this out into a separate decorator for notebooks.
|
514
|
-
mounts: Collection[_Mount] = (),
|
515
508
|
network_file_systems: dict[Union[str, PurePosixPath], _NetworkFileSystem] = {},
|
516
509
|
volumes: dict[Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]] = {},
|
517
510
|
webhook_config: Optional[api_pb2.WebhookConfig] = None,
|
@@ -567,8 +560,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
567
560
|
assert not webhook_config
|
568
561
|
assert not schedule
|
569
562
|
|
570
|
-
explicit_mounts = mounts
|
571
|
-
|
572
563
|
include_source_mode = get_include_source_mode(include_source)
|
573
564
|
if include_source_mode != IncludeSourceMode.INCLUDE_NOTHING:
|
574
565
|
entrypoint_mounts = info.get_entrypoint_mount()
|
@@ -577,34 +568,9 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
577
568
|
|
578
569
|
all_mounts = [
|
579
570
|
_get_client_mount(),
|
580
|
-
*explicit_mounts,
|
581
571
|
*entrypoint_mounts.values(),
|
582
572
|
]
|
583
573
|
|
584
|
-
if include_source_mode is IncludeSourceMode.INCLUDE_FIRST_PARTY and is_local():
|
585
|
-
auto_mounts = get_sys_modules_mounts()
|
586
|
-
# don't need to add entrypoint modules to automounts:
|
587
|
-
for entrypoint_module in entrypoint_mounts:
|
588
|
-
auto_mounts.pop(entrypoint_module, None)
|
589
|
-
|
590
|
-
warn_missing_modules = set(auto_mounts.keys()) - image._added_python_source_set
|
591
|
-
|
592
|
-
if warn_missing_modules:
|
593
|
-
python_stringified_modules = ", ".join(f'"{mod}"' for mod in sorted(warn_missing_modules))
|
594
|
-
deprecation_warning(
|
595
|
-
(2025, 2, 3),
|
596
|
-
(
|
597
|
-
'Modal will stop implicitly adding local Python modules to the Image ("automounting") in a '
|
598
|
-
"future update. The following modules need to be explicitly added for future "
|
599
|
-
"compatibility:\n"
|
600
|
-
)
|
601
|
-
+ "\n".join(sorted([f"* {m}" for m in warn_missing_modules]))
|
602
|
-
+ "\n\n"
|
603
|
-
+ (f"e.g.:\nimage_with_source = my_image.add_local_python_source({python_stringified_modules})\n\n")
|
604
|
-
+ "For more information, see https://modal.com/docs/guide/modal-1-0-migration",
|
605
|
-
)
|
606
|
-
all_mounts += auto_mounts.values()
|
607
|
-
|
608
574
|
retry_policy = _parse_retries(
|
609
575
|
retries, f"Function '{info.get_tag()}'" if info.raw_f else f"Class '{info.get_tag()}'"
|
610
576
|
)
|
@@ -641,7 +607,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
641
607
|
image=image,
|
642
608
|
secrets=secrets,
|
643
609
|
gpu=gpu,
|
644
|
-
mounts=mounts,
|
645
610
|
network_file_systems=network_file_systems,
|
646
611
|
volumes=volumes,
|
647
612
|
memory=memory,
|
@@ -749,16 +714,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
749
714
|
|
750
715
|
def _deps(only_explicit_mounts=False) -> list[_Object]:
|
751
716
|
deps: list[_Object] = list(secrets)
|
752
|
-
if only_explicit_mounts:
|
753
|
-
# TODO: this is a bit hacky, but all_mounts may differ in the container vs locally
|
754
|
-
# We don't want the function dependencies to change, so we have this way to force it to
|
755
|
-
# only include its declared dependencies.
|
756
|
-
# Only objects that need interaction within a user's container actually need to be
|
757
|
-
# included when only_explicit_mounts=True, so omitting auto mounts here
|
758
|
-
# wouldn't be a problem as long as Mounts are "passive" and only loaded by the
|
759
|
-
# worker runtime
|
760
|
-
deps += list(explicit_mounts)
|
761
|
-
else:
|
717
|
+
if not only_explicit_mounts:
|
762
718
|
deps += list(all_mounts)
|
763
719
|
if proxy:
|
764
720
|
deps.append(proxy)
|
@@ -1026,7 +982,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1026
982
|
obj._build_args = dict( # See get_build_def
|
1027
983
|
secrets=repr(secrets),
|
1028
984
|
gpu_config=repr([parse_gpu_config(_gpu) for _gpu in gpus]),
|
1029
|
-
mounts=repr(mounts),
|
1030
985
|
network_file_systems=repr(network_file_systems),
|
1031
986
|
)
|
1032
987
|
# these key are excluded if empty to avoid rebuilds on client upgrade
|
@@ -1190,7 +1145,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1190
1145
|
|
1191
1146
|
@live_method
|
1192
1147
|
async def keep_warm(self, warm_pool_size: int) -> None:
|
1193
|
-
"""
|
1148
|
+
"""mdmd:hidden
|
1149
|
+
Set the warm pool size for the Function.
|
1194
1150
|
|
1195
1151
|
DEPRECATED: Please adapt your code to use the more general `update_autoscaler` method instead:
|
1196
1152
|
|
@@ -1255,7 +1211,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1255
1211
|
return cls._from_loader(_load_remote, rep, is_another_app=True, hydrate_lazily=True)
|
1256
1212
|
|
1257
1213
|
@classmethod
|
1258
|
-
@renamed_parameter((2024, 12, 18), "tag", "name")
|
1259
1214
|
def from_name(
|
1260
1215
|
cls: type["_Function"],
|
1261
1216
|
app_name: str,
|
@@ -1288,7 +1243,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1288
1243
|
return cls._from_name(app_name, name, namespace, environment_name)
|
1289
1244
|
|
1290
1245
|
@staticmethod
|
1291
|
-
@renamed_parameter((2024, 12, 18), "tag", "name")
|
1292
1246
|
async def lookup(
|
1293
1247
|
app_name: str,
|
1294
1248
|
name: str,
|
@@ -1296,7 +1250,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1296
1250
|
client: Optional[_Client] = None,
|
1297
1251
|
environment_name: Optional[str] = None,
|
1298
1252
|
) -> "_Function":
|
1299
|
-
"""
|
1253
|
+
"""mdmd:hidden
|
1254
|
+
Lookup a Function from a deployed App by its name.
|
1300
1255
|
|
1301
1256
|
DEPRECATED: This method is deprecated in favor of `modal.Function.from_name`.
|
1302
1257
|
|
@@ -1420,7 +1375,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1420
1375
|
@property
|
1421
1376
|
@live_method
|
1422
1377
|
async def web_url(self) -> Optional[str]:
|
1423
|
-
"""
|
1378
|
+
"""mdmd:hidden
|
1379
|
+
Deprecated. Use the `Function.get_web_url()` method instead.
|
1424
1380
|
|
1425
1381
|
URL of a Function running as a web endpoint.
|
1426
1382
|
"""
|
@@ -1537,20 +1493,6 @@ Use the `Function.get_web_url()` method instead.
|
|
1537
1493
|
async for res in invocation.run_generator():
|
1538
1494
|
yield res
|
1539
1495
|
|
1540
|
-
@synchronizer.no_io_translation
|
1541
|
-
async def _call_generator_nowait(self, args, kwargs):
|
1542
|
-
deprecation_warning(
|
1543
|
-
(2024, 12, 11),
|
1544
|
-
"Calling spawn on a generator function is deprecated and will soon raise an exception.",
|
1545
|
-
)
|
1546
|
-
return await _Invocation.create(
|
1547
|
-
self,
|
1548
|
-
args,
|
1549
|
-
kwargs,
|
1550
|
-
client=self.client,
|
1551
|
-
function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_ASYNC,
|
1552
|
-
)
|
1553
|
-
|
1554
1496
|
@synchronizer.no_io_translation
|
1555
1497
|
@live_method
|
1556
1498
|
async def remote(self, *args: P.args, **kwargs: P.kwargs) -> ReturnType:
|
@@ -1663,7 +1605,7 @@ Use the `Function.get_web_url()` method instead.
|
|
1663
1605
|
"""
|
1664
1606
|
self._check_no_web_url("_experimental_spawn")
|
1665
1607
|
if self._is_generator:
|
1666
|
-
|
1608
|
+
raise InvalidError("Cannot `spawn` a generator function.")
|
1667
1609
|
else:
|
1668
1610
|
invocation = await self._call_function_nowait(
|
1669
1611
|
args, kwargs, function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_ASYNC
|
@@ -1695,14 +1637,13 @@ Use the `Function.get_web_url()` method instead.
|
|
1695
1637
|
"""
|
1696
1638
|
self._check_no_web_url("spawn")
|
1697
1639
|
if self._is_generator:
|
1698
|
-
|
1640
|
+
raise InvalidError("Cannot `spawn` a generator function.")
|
1699
1641
|
else:
|
1700
1642
|
invocation = await self._call_function_nowait(args, kwargs, api_pb2.FUNCTION_CALL_INVOCATION_TYPE_ASYNC)
|
1701
1643
|
|
1702
1644
|
fc: _FunctionCall[ReturnType] = _FunctionCall._new_hydrated(
|
1703
1645
|
invocation.function_call_id, invocation.client, None
|
1704
1646
|
)
|
1705
|
-
fc._is_generator = self._is_generator if self._is_generator else False
|
1706
1647
|
return fc
|
1707
1648
|
|
1708
1649
|
def get_raw_f(self) -> Callable[..., Any]:
|
@@ -1761,22 +1702,8 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
1761
1702
|
|
1762
1703
|
The returned coroutine is not cancellation-safe.
|
1763
1704
|
"""
|
1764
|
-
|
1765
|
-
if self._is_generator:
|
1766
|
-
raise Exception("Cannot get the result of a generator function call. Use `get_gen` instead.")
|
1767
|
-
|
1768
1705
|
return await self._invocation().poll_function(timeout=timeout)
|
1769
1706
|
|
1770
|
-
async def get_gen(self) -> AsyncGenerator[Any, None]:
|
1771
|
-
"""
|
1772
|
-
Calls the generator remotely, executing it with the given arguments and returning the execution's result.
|
1773
|
-
"""
|
1774
|
-
if not self._is_generator:
|
1775
|
-
raise Exception("Cannot iterate over a non-generator function call. Use `get` instead.")
|
1776
|
-
|
1777
|
-
async for res in self._invocation().run_generator():
|
1778
|
-
yield res
|
1779
|
-
|
1780
1707
|
async def get_call_graph(self) -> list[InputInfo]:
|
1781
1708
|
"""Returns a structure representing the call graph from a given root
|
1782
1709
|
call ID, along with the status of execution for each node.
|
@@ -1807,9 +1734,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
1807
1734
|
await retry_transient_errors(self._client.stub.FunctionCallCancel, request)
|
1808
1735
|
|
1809
1736
|
@staticmethod
|
1810
|
-
async def from_id(
|
1811
|
-
function_call_id: str, client: Optional[_Client] = None, is_generator: bool = False
|
1812
|
-
) -> "_FunctionCall[Any]":
|
1737
|
+
async def from_id(function_call_id: str, client: Optional[_Client] = None) -> "_FunctionCall[Any]":
|
1813
1738
|
"""Instantiate a FunctionCall object from an existing ID.
|
1814
1739
|
|
1815
1740
|
Examples:
|
@@ -1832,7 +1757,6 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
1832
1757
|
client = await _Client.from_env()
|
1833
1758
|
|
1834
1759
|
fc: _FunctionCall[Any] = _FunctionCall._new_hydrated(function_call_id, client, None)
|
1835
|
-
fc._is_generator = is_generator
|
1836
1760
|
return fc
|
1837
1761
|
|
1838
1762
|
@staticmethod
|
@@ -1863,7 +1787,8 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
1863
1787
|
|
1864
1788
|
|
1865
1789
|
async def _gather(*function_calls: _FunctionCall[T]) -> typing.Sequence[T]:
|
1866
|
-
"""
|
1790
|
+
"""mdmd:hidden
|
1791
|
+
Deprecated: Please use `modal.FunctionCall.gather()` instead."""
|
1867
1792
|
deprecation_warning(
|
1868
1793
|
(2025, 2, 24),
|
1869
1794
|
"`modal.functions.gather()` is deprecated; please use `modal.FunctionCall.gather()` instead.",
|
modal/_object.py
CHANGED
@@ -10,7 +10,6 @@ from typing_extensions import Self
|
|
10
10
|
|
11
11
|
from ._resolver import Resolver
|
12
12
|
from ._utils.async_utils import aclosing
|
13
|
-
from ._utils.deprecation import deprecation_warning
|
14
13
|
from .client import _Client
|
15
14
|
from .config import config, logger
|
16
15
|
from .exception import ExecutionError, InvalidError
|
@@ -237,19 +236,6 @@ class _Object:
|
|
237
236
|
|
238
237
|
return self._deps if self._deps is not None else default_deps
|
239
238
|
|
240
|
-
async def resolve(self, client: Optional[_Client] = None):
|
241
|
-
"""mdmd:hidden"""
|
242
|
-
obj = self.__class__.__name__.strip("_")
|
243
|
-
deprecation_warning(
|
244
|
-
(2025, 1, 16),
|
245
|
-
f"The `{obj}.resolve` method is deprecated and will be removed in a future release."
|
246
|
-
f" Please use `{obj}.hydrate()` or `await {obj}.hydrate.aio()` instead."
|
247
|
-
"\n\nNote that it is rarely necessary to explicitly hydrate objects, as most methods"
|
248
|
-
" will lazily hydrate when needed.",
|
249
|
-
show_source=False, # synchronicity interferes with attributing source correctly
|
250
|
-
)
|
251
|
-
await self.hydrate(client)
|
252
|
-
|
253
239
|
async def hydrate(self, client: Optional[_Client] = None) -> Self:
|
254
240
|
"""Synchronize the local object with its identity on the Modal server.
|
255
241
|
|
modal/_partial_function.py
CHANGED
@@ -18,7 +18,7 @@ from modal_proto import api_pb2
|
|
18
18
|
from ._functions import _Function
|
19
19
|
from ._utils.async_utils import synchronizer
|
20
20
|
from ._utils.deprecation import deprecation_warning
|
21
|
-
from ._utils.function_utils import
|
21
|
+
from ._utils.function_utils import callable_has_non_self_params
|
22
22
|
from .config import logger
|
23
23
|
from .exception import InvalidError
|
24
24
|
|
@@ -181,21 +181,11 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
181
181
|
|
182
182
|
if require_sync and inspect.iscoroutinefunction(self.raw_f):
|
183
183
|
self.registered = True # Hacky, avoid false-positive warning
|
184
|
-
raise InvalidError(f"`@modal.{decorator_name}` can't be applied to an async function.")
|
184
|
+
raise InvalidError(f"The `@modal.{decorator_name}` decorator can't be applied to an async function.")
|
185
185
|
|
186
186
|
if require_nullary and callable_has_non_self_params(self.raw_f):
|
187
187
|
self.registered = True # Hacky, avoid false-positive warning
|
188
|
-
|
189
|
-
raise InvalidError(f"Functions obj by `@modal.{decorator_name}` can't have parameters.")
|
190
|
-
else:
|
191
|
-
# TODO(michael): probably fine to just make this an error at this point
|
192
|
-
# but best to do it in a separate PR
|
193
|
-
deprecation_warning(
|
194
|
-
(2024, 9, 4),
|
195
|
-
f"The function obj by `@modal.{decorator_name}` has default parameters, "
|
196
|
-
"but shouldn't have any parameters - Modal will drop support for "
|
197
|
-
"default parameters in a future release.",
|
198
|
-
)
|
188
|
+
raise InvalidError(f"Functions decorated by `@modal.{decorator_name}` can't have parameters.")
|
199
189
|
|
200
190
|
def _get_raw_f(self) -> Callable[P, ReturnType]:
|
201
191
|
assert self.raw_f is not None
|
@@ -659,7 +649,7 @@ def _web_server(
|
|
659
649
|
def _build(
|
660
650
|
_warn_parentheses_missing=None, *, force: bool = False, timeout: int = 86400
|
661
651
|
) -> Callable[[Union[_PartialFunction, NullaryMethod]], _PartialFunction]:
|
662
|
-
"""
|
652
|
+
"""mdmd:hidden
|
663
653
|
Decorator for methods that execute at _build time_ to create a new Image layer.
|
664
654
|
|
665
655
|
**Deprecated**: This function is deprecated. We recommend using `modal.Volume`
|
modal/_serialization.py
CHANGED
@@ -324,8 +324,8 @@ def _deserialize_asgi(asgi: api_pb2.Asgi) -> Any:
|
|
324
324
|
elif msg_type == "websocket_send":
|
325
325
|
return {
|
326
326
|
"type": "websocket.send",
|
327
|
-
"bytes": asgi.websocket_send.bytes if asgi.websocket_send.HasField("bytes") else
|
328
|
-
"text": asgi.websocket_send.text if asgi.websocket_send.HasField("text") else
|
327
|
+
**({"bytes": asgi.websocket_send.bytes} if asgi.websocket_send.HasField("bytes") else {}),
|
328
|
+
**({"text": asgi.websocket_send.text} if asgi.websocket_send.HasField("text") else {}),
|
329
329
|
}
|
330
330
|
elif msg_type == "websocket_disconnect":
|
331
331
|
return {
|
modal/_utils/function_utils.py
CHANGED
@@ -23,7 +23,7 @@ from .._serialization import (
|
|
23
23
|
signature_to_parameter_specs,
|
24
24
|
)
|
25
25
|
from .._traceback import append_modal_tb
|
26
|
-
from ..config import
|
26
|
+
from ..config import logger
|
27
27
|
from ..exception import (
|
28
28
|
DeserializationError,
|
29
29
|
ExecutionError,
|
@@ -627,8 +627,7 @@ class FunctionCreationStatus:
|
|
627
627
|
|
628
628
|
class IncludeSourceMode(enum.Enum):
|
629
629
|
INCLUDE_NOTHING = False # can only be set in source, can't be set in config
|
630
|
-
INCLUDE_MAIN_PACKAGE = True #
|
631
|
-
INCLUDE_FIRST_PARTY = "legacy" # mounts all "local" modules in sys.modules - represented by AUTOMOUNT=1 in config
|
630
|
+
INCLUDE_MAIN_PACKAGE = True # Default behavior
|
632
631
|
|
633
632
|
|
634
633
|
def get_include_source_mode(function_or_app_specific) -> IncludeSourceMode:
|
@@ -650,6 +649,4 @@ def get_include_source_mode(function_or_app_specific) -> IncludeSourceMode:
|
|
650
649
|
# explicitly set in app/function
|
651
650
|
return IncludeSourceMode(function_or_app_specific)
|
652
651
|
|
653
|
-
|
654
|
-
legacy_automount_mode: bool = config.get("automount")
|
655
|
-
return IncludeSourceMode.INCLUDE_FIRST_PARTY if legacy_automount_mode else IncludeSourceMode.INCLUDE_MAIN_PACKAGE
|
652
|
+
return IncludeSourceMode.INCLUDE_MAIN_PACKAGE
|
modal/_utils/grpc_utils.py
CHANGED
@@ -148,15 +148,20 @@ def create_channel(
|
|
148
148
|
|
149
149
|
logger.debug(f"Sending request to {event.method_name}")
|
150
150
|
|
151
|
-
async def
|
151
|
+
async def recv_initial_metadata(initial_metadata: grpclib.events.RecvInitialMetadata) -> None:
|
152
152
|
# If we receive an auth token from the server, include it in all future requests.
|
153
153
|
# TODO(nathan): This isn't perfect because the metadata isn't propagated when the
|
154
154
|
# process is forked and a new channel is created. This is OK for now since this
|
155
155
|
# token is only used by the experimental input plane
|
156
|
+
if token := initial_metadata.metadata.get("x-modal-auth-token"):
|
157
|
+
metadata["x-modal-auth-token"] = str(token)
|
158
|
+
|
159
|
+
async def recv_trailing_metadata(trailing_metadata: grpclib.events.RecvTrailingMetadata) -> None:
|
156
160
|
if token := trailing_metadata.metadata.get("x-modal-auth-token"):
|
157
161
|
metadata["x-modal-auth-token"] = str(token)
|
158
162
|
|
159
163
|
grpclib.events.listen(channel, grpclib.events.SendRequest, send_request)
|
164
|
+
grpclib.events.listen(channel, grpclib.events.RecvInitialMetadata, recv_initial_metadata)
|
160
165
|
grpclib.events.listen(channel, grpclib.events.RecvTrailingMetadata, recv_trailing_metadata)
|
161
166
|
|
162
167
|
return channel
|