modal 0.73.27__py3-none-any.whl → 0.73.29__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/__main__.py +1 -1
- modal/_container_entrypoint.py +4 -4
- modal/_functions.py +6 -5
- modal/_partial_function.py +691 -0
- modal/_resolver.py +1 -2
- modal/_runtime/container_io_manager.py +1 -1
- modal/_runtime/user_code_imports.py +3 -4
- modal/_utils/async_utils.py +3 -6
- modal/_utils/blob_utils.py +1 -1
- modal/_utils/function_utils.py +1 -2
- modal/app.py +12 -14
- modal/cli/entry_point.py +1 -1
- modal/cli/run.py +2 -3
- modal/cli/secret.py +1 -1
- modal/cli/volume.py +1 -2
- modal/client.pyi +2 -2
- modal/cls.py +7 -8
- modal/cls.pyi +2 -1
- modal/environments.py +1 -3
- modal/experimental.py +1 -1
- modal/file_pattern_matcher.py +1 -2
- modal/mount.py +4 -8
- modal/output.py +1 -0
- modal/partial_function.py +26 -696
- modal/partial_function.pyi +19 -157
- modal/sandbox.py +4 -8
- modal/token_flow.py +1 -1
- {modal-0.73.27.dist-info → modal-0.73.29.dist-info}/METADATA +1 -1
- {modal-0.73.27.dist-info → modal-0.73.29.dist-info}/RECORD +35 -34
- modal_docs/mdmd/mdmd.py +1 -0
- modal_version/_version_generated.py +1 -1
- {modal-0.73.27.dist-info → modal-0.73.29.dist-info}/LICENSE +0 -0
- {modal-0.73.27.dist-info → modal-0.73.29.dist-info}/WHEEL +0 -0
- {modal-0.73.27.dist-info → modal-0.73.29.dist-info}/entry_points.txt +0 -0
- {modal-0.73.27.dist-info → modal-0.73.29.dist-info}/top_level.txt +0 -0
modal/_resolver.py
CHANGED
@@ -142,8 +142,7 @@ class Resolver:
|
|
142
142
|
and obj.object_id != existing_object_id
|
143
143
|
):
|
144
144
|
raise Exception(
|
145
|
-
f"Tried creating an object using existing id {existing_object_id}"
|
146
|
-
f" but it has id {obj.object_id}"
|
145
|
+
f"Tried creating an object using existing id {existing_object_id} but it has id {obj.object_id}"
|
147
146
|
)
|
148
147
|
|
149
148
|
return obj
|
@@ -896,7 +896,7 @@ class _ContainerIOManager:
|
|
896
896
|
gpu_process_state = gpu_memory_snapshot.get_state()
|
897
897
|
if gpu_process_state != gpu_memory_snapshot.CudaCheckpointState.RUNNING:
|
898
898
|
raise ValueError(
|
899
|
-
"Cannot snapshot GPU state if it isn't running.
|
899
|
+
f"Cannot snapshot GPU state if it isn't running. Current GPU state: {gpu_process_state}"
|
900
900
|
)
|
901
901
|
|
902
902
|
gpu_memory_snapshot.toggle()
|
@@ -10,10 +10,10 @@ import modal._runtime.container_io_manager
|
|
10
10
|
import modal.cls
|
11
11
|
from modal import Function
|
12
12
|
from modal._functions import _Function
|
13
|
+
from modal._partial_function import _find_partial_methods_for_user_cls, _PartialFunctionFlags
|
13
14
|
from modal._utils.async_utils import synchronizer
|
14
15
|
from modal._utils.function_utils import LocalFunctionError, is_async as get_is_async, is_global_object
|
15
16
|
from modal.exception import ExecutionError, InvalidError
|
16
|
-
from modal.partial_function import _find_partial_methods_for_user_cls, _PartialFunctionFlags
|
17
17
|
from modal_proto import api_pb2
|
18
18
|
|
19
19
|
if typing.TYPE_CHECKING:
|
@@ -46,8 +46,7 @@ class Service(metaclass=ABCMeta):
|
|
46
46
|
@abstractmethod
|
47
47
|
def get_finalized_functions(
|
48
48
|
self, fun_def: api_pb2.Function, container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager"
|
49
|
-
) -> dict[str, "FinalizedFunction"]:
|
50
|
-
...
|
49
|
+
) -> dict[str, "FinalizedFunction"]: ...
|
51
50
|
|
52
51
|
|
53
52
|
def construct_webhook_callable(
|
@@ -139,7 +138,7 @@ class ImportedClass(Service):
|
|
139
138
|
app: Optional["modal.app._App"]
|
140
139
|
code_deps: Optional[Sequence["modal._object._Object"]]
|
141
140
|
|
142
|
-
_partial_functions: dict[str, "modal.
|
141
|
+
_partial_functions: dict[str, "modal._partial_function._PartialFunction"]
|
143
142
|
|
144
143
|
def get_finalized_functions(
|
145
144
|
self, fun_def: api_pb2.Function, container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager"
|
modal/_utils/async_utils.py
CHANGED
@@ -505,13 +505,11 @@ async def sync_or_async_iter(iter: Union[Iterable[T], AsyncIterable[T]]) -> Asyn
|
|
505
505
|
|
506
506
|
|
507
507
|
@typing.overload
|
508
|
-
def async_zip(g1: AsyncGenerator[T, None], g2: AsyncGenerator[V, None], /) -> AsyncGenerator[tuple[T, V], None]:
|
509
|
-
...
|
508
|
+
def async_zip(g1: AsyncGenerator[T, None], g2: AsyncGenerator[V, None], /) -> AsyncGenerator[tuple[T, V], None]: ...
|
510
509
|
|
511
510
|
|
512
511
|
@typing.overload
|
513
|
-
def async_zip(*generators: AsyncGenerator[T, None]) -> AsyncGenerator[tuple[T, ...], None]:
|
514
|
-
...
|
512
|
+
def async_zip(*generators: AsyncGenerator[T, None]) -> AsyncGenerator[tuple[T, ...], None]: ...
|
515
513
|
|
516
514
|
|
517
515
|
async def async_zip(*generators):
|
@@ -561,8 +559,7 @@ class ExceptionWrapper:
|
|
561
559
|
value: Exception
|
562
560
|
|
563
561
|
|
564
|
-
class StopSentinelType:
|
565
|
-
...
|
562
|
+
class StopSentinelType: ...
|
566
563
|
|
567
564
|
|
568
565
|
STOP_SENTINEL = StopSentinelType()
|
modal/_utils/blob_utils.py
CHANGED
@@ -227,7 +227,7 @@ async def blob_upload(payload: bytes, stub: ModalClientModal) -> str:
|
|
227
227
|
blob_id = await _blob_upload(upload_hashes, payload, stub)
|
228
228
|
dur_s = max(time.time() - t0, 0.001) # avoid division by zero
|
229
229
|
throughput_mib_s = (size_mib) / dur_s
|
230
|
-
logger.debug(f"Uploaded large blob of size {size_mib:.2f} MiB ({throughput_mib_s:.2f} MiB/s).
|
230
|
+
logger.debug(f"Uploaded large blob of size {size_mib:.2f} MiB ({throughput_mib_s:.2f} MiB/s). {blob_id}")
|
231
231
|
return blob_id
|
232
232
|
|
233
233
|
|
modal/_utils/function_utils.py
CHANGED
@@ -246,7 +246,6 @@ class FunctionInfo:
|
|
246
246
|
|
247
247
|
def get_cls_var_attrs(self) -> dict[str, Any]:
|
248
248
|
import dis
|
249
|
-
|
250
249
|
import opcode
|
251
250
|
|
252
251
|
LOAD_ATTR = opcode.opmap["LOAD_ATTR"]
|
@@ -603,7 +602,7 @@ class FunctionCreationStatus:
|
|
603
602
|
for custom_domain in self.response.function.custom_domain_info:
|
604
603
|
custom_domain_status_row = self.resolver.add_status_row()
|
605
604
|
custom_domain_status_row.finish(
|
606
|
-
f"Custom domain for {self.tag} => [magenta underline]
|
605
|
+
f"Custom domain for {self.tag} => [magenta underline]{custom_domain.url}[/magenta underline]"
|
607
606
|
)
|
608
607
|
else:
|
609
608
|
self.status_row.finish(f"Created function {self.tag}.")
|
modal/app.py
CHANGED
@@ -23,6 +23,11 @@ from modal_proto import api_pb2
|
|
23
23
|
from ._functions import _Function
|
24
24
|
from ._ipython import is_notebook
|
25
25
|
from ._object import _get_environment_name, _Object
|
26
|
+
from ._partial_function import (
|
27
|
+
_find_partial_methods_for_user_cls,
|
28
|
+
_PartialFunction,
|
29
|
+
_PartialFunctionFlags,
|
30
|
+
)
|
26
31
|
from ._utils.async_utils import synchronize_api
|
27
32
|
from ._utils.deprecation import deprecation_error, deprecation_warning, renamed_parameter
|
28
33
|
from ._utils.function_utils import FunctionInfo, is_global_object, is_method_fn
|
@@ -38,12 +43,7 @@ from .gpu import GPU_T
|
|
38
43
|
from .image import _Image
|
39
44
|
from .mount import _Mount
|
40
45
|
from .network_file_system import _NetworkFileSystem
|
41
|
-
from .partial_function import
|
42
|
-
PartialFunction,
|
43
|
-
_find_partial_methods_for_user_cls,
|
44
|
-
_PartialFunction,
|
45
|
-
_PartialFunctionFlags,
|
46
|
-
)
|
46
|
+
from .partial_function import PartialFunction
|
47
47
|
from .proxy import _Proxy
|
48
48
|
from .retries import Retries
|
49
49
|
from .running_app import RunningApp
|
@@ -102,21 +102,19 @@ class _FunctionDecoratorType:
|
|
102
102
|
@overload
|
103
103
|
def __call__(
|
104
104
|
self, func: PartialFunction[P, ReturnType, OriginalReturnType]
|
105
|
-
) -> Function[P, ReturnType, OriginalReturnType]:
|
106
|
-
... # already wrapped by a modal decorator, e.g. web_endpoint
|
105
|
+
) -> Function[P, ReturnType, OriginalReturnType]: ... # already wrapped by a modal decorator, e.g. web_endpoint
|
107
106
|
|
108
107
|
@overload
|
109
108
|
def __call__(
|
110
109
|
self, func: Callable[P, Coroutine[Any, Any, ReturnType]]
|
111
|
-
) -> Function[P, ReturnType, Coroutine[Any, Any, ReturnType]]:
|
112
|
-
... # decorated async function
|
110
|
+
) -> Function[P, ReturnType, Coroutine[Any, Any, ReturnType]]: ... # decorated async function
|
113
111
|
|
114
112
|
@overload
|
115
|
-
def __call__(
|
116
|
-
|
113
|
+
def __call__(
|
114
|
+
self, func: Callable[P, ReturnType]
|
115
|
+
) -> Function[P, ReturnType, ReturnType]: ... # decorated non-async function
|
117
116
|
|
118
|
-
def __call__(self, func):
|
119
|
-
...
|
117
|
+
def __call__(self, func): ...
|
120
118
|
|
121
119
|
|
122
120
|
class _App:
|
modal/cli/entry_point.py
CHANGED
@@ -69,7 +69,7 @@ def check_path():
|
|
69
69
|
"[red]The `[white]modal[/white]` command is not executable!\n"
|
70
70
|
"You may need to give it permissions or use `[white]python -m modal[/white]` as a workaround.[/red]\n"
|
71
71
|
)
|
72
|
-
text += "See more information here:\n\n
|
72
|
+
text += f"See more information here:\n\n[link={url}]{url}[/link]\n"
|
73
73
|
console = Console()
|
74
74
|
console.print(text)
|
75
75
|
console.print(Rule(style="white"))
|
modal/cli/run.py
CHANGED
@@ -230,8 +230,7 @@ def _get_click_command_for_cls(app: App, method_ref: MethodReference):
|
|
230
230
|
method_name = method_names[0]
|
231
231
|
else:
|
232
232
|
raise click.UsageError(
|
233
|
-
f"Please specify a specific method of {cls._get_name()} to run, "
|
234
|
-
f"e.g. `modal run foo.py::MyClass.bar`" # noqa: E501
|
233
|
+
f"Please specify a specific method of {cls._get_name()} to run, e.g. `modal run foo.py::MyClass.bar`" # noqa: E501
|
235
234
|
)
|
236
235
|
|
237
236
|
partial_function = partial_functions[method_name]
|
@@ -493,7 +492,7 @@ def shell(
|
|
493
492
|
cloud: Optional[str] = typer.Option(
|
494
493
|
default=None,
|
495
494
|
help=(
|
496
|
-
"Cloud provider to run the shell on.
|
495
|
+
"Cloud provider to run the shell on. Possible values are `aws`, `gcp`, `oci`, `auto` (if not using REF)."
|
497
496
|
),
|
498
497
|
),
|
499
498
|
region: Optional[str] = typer.Option(
|
modal/cli/secret.py
CHANGED
@@ -85,7 +85,7 @@ def some_function():
|
|
85
85
|
"""
|
86
86
|
plural_s = "s" if len(env_dict) > 1 else ""
|
87
87
|
console.print(
|
88
|
-
f"""Created a new secret '{secret_name}' with the key{plural_s} {
|
88
|
+
f"""Created a new secret '{secret_name}' with the key{plural_s} {", ".join(repr(k) for k in env_dict.keys())}"""
|
89
89
|
)
|
90
90
|
console.print("\nUse it in to your Modal app using:\n")
|
91
91
|
console.print(Syntax(example_code, "python"))
|
modal/cli/volume.py
CHANGED
@@ -297,8 +297,7 @@ async def rename(
|
|
297
297
|
):
|
298
298
|
if not yes:
|
299
299
|
typer.confirm(
|
300
|
-
f"Are you sure you want rename the modal.Volume '{old_name}'?"
|
301
|
-
" This may break any Apps currently using it.",
|
300
|
+
f"Are you sure you want rename the modal.Volume '{old_name}'? This may break any Apps currently using it.",
|
302
301
|
default=False,
|
303
302
|
abort=True,
|
304
303
|
)
|
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.73.
|
30
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.29"
|
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.73.
|
88
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.29"
|
89
89
|
): ...
|
90
90
|
def is_closed(self) -> bool: ...
|
91
91
|
@property
|
modal/cls.py
CHANGED
@@ -13,6 +13,12 @@ from modal_proto import api_pb2
|
|
13
13
|
|
14
14
|
from ._functions import _Function, _parse_retries
|
15
15
|
from ._object import _Object
|
16
|
+
from ._partial_function import (
|
17
|
+
_find_callables_for_obj,
|
18
|
+
_find_partial_methods_for_user_cls,
|
19
|
+
_PartialFunction,
|
20
|
+
_PartialFunctionFlags,
|
21
|
+
)
|
16
22
|
from ._resolver import Resolver
|
17
23
|
from ._resources import convert_fn_config_to_resources_config
|
18
24
|
from ._serialization import check_valid_cls_constructor_arg
|
@@ -25,12 +31,6 @@ from .client import _Client
|
|
25
31
|
from .config import config
|
26
32
|
from .exception import ExecutionError, InvalidError, NotFoundError
|
27
33
|
from .gpu import GPU_T
|
28
|
-
from .partial_function import (
|
29
|
-
_find_callables_for_obj,
|
30
|
-
_find_partial_methods_for_user_cls,
|
31
|
-
_PartialFunction,
|
32
|
-
_PartialFunctionFlags,
|
33
|
-
)
|
34
34
|
from .retries import Retries
|
35
35
|
from .secret import _Secret
|
36
36
|
from .volume import _Volume
|
@@ -674,8 +674,7 @@ class _Cls(_Object, type_prefix="cs"):
|
|
674
674
|
)
|
675
675
|
|
676
676
|
def __getattr__(self, k):
|
677
|
-
#
|
678
|
-
# TODO: remove this method - access to attributes on classes should be discouraged
|
677
|
+
# TODO: remove this method - access to attributes on classes (not instances) should be discouraged
|
679
678
|
if k in self._method_functions:
|
680
679
|
deprecation_warning(
|
681
680
|
(2025, 1, 13),
|
modal/cls.pyi
CHANGED
@@ -3,6 +3,7 @@ import google.protobuf.message
|
|
3
3
|
import inspect
|
4
4
|
import modal._functions
|
5
5
|
import modal._object
|
6
|
+
import modal._partial_function
|
6
7
|
import modal.app
|
7
8
|
import modal.client
|
8
9
|
import modal.functions
|
@@ -104,7 +105,7 @@ class _Cls(modal._object._Object):
|
|
104
105
|
|
105
106
|
def _initialize_from_empty(self): ...
|
106
107
|
def _initialize_from_other(self, other: _Cls): ...
|
107
|
-
def _get_partial_functions(self) -> dict[str, modal.
|
108
|
+
def _get_partial_functions(self) -> dict[str, modal._partial_function._PartialFunction]: ...
|
108
109
|
def _get_app(self) -> modal.app._App: ...
|
109
110
|
def _get_user_cls(self) -> type: ...
|
110
111
|
def _get_name(self) -> str: ...
|
modal/environments.py
CHANGED
@@ -29,9 +29,7 @@ class _Environment(_Object, type_prefix="en"):
|
|
29
29
|
|
30
30
|
def __init__(self):
|
31
31
|
"""mdmd:hidden"""
|
32
|
-
raise RuntimeError(
|
33
|
-
"`Environment(...)` constructor is not allowed." " Please use `Environment.from_name` instead."
|
34
|
-
)
|
32
|
+
raise RuntimeError("`Environment(...)` constructor is not allowed. Please use `Environment.from_name` instead.")
|
35
33
|
|
36
34
|
# TODO(michael) Keeping this private for now until we decide what else should be in it
|
37
35
|
# And what the rules should be about updates / mutability
|
modal/experimental.py
CHANGED
@@ -7,11 +7,11 @@ from modal_proto import api_pb2
|
|
7
7
|
from ._clustered_functions import ClusterInfo, get_cluster_info as _get_cluster_info
|
8
8
|
from ._functions import _Function
|
9
9
|
from ._object import _get_environment_name
|
10
|
+
from ._partial_function import _PartialFunction, _PartialFunctionFlags
|
10
11
|
from ._runtime.container_io_manager import _ContainerIOManager
|
11
12
|
from ._utils.async_utils import synchronizer
|
12
13
|
from .client import _Client
|
13
14
|
from .exception import InvalidError
|
14
|
-
from .partial_function import _PartialFunction, _PartialFunctionFlags
|
15
15
|
|
16
16
|
|
17
17
|
def stop_fetching_inputs():
|
modal/file_pattern_matcher.py
CHANGED
modal/mount.py
CHANGED
@@ -78,20 +78,16 @@ def python_standalone_mount_name(version: str) -> str:
|
|
78
78
|
|
79
79
|
class _MountEntry(metaclass=abc.ABCMeta):
|
80
80
|
@abc.abstractmethod
|
81
|
-
def description(self) -> str:
|
82
|
-
...
|
81
|
+
def description(self) -> str: ...
|
83
82
|
|
84
83
|
@abc.abstractmethod
|
85
|
-
def get_files_to_upload(self) -> typing.Iterator[tuple[Path, str]]:
|
86
|
-
...
|
84
|
+
def get_files_to_upload(self) -> typing.Iterator[tuple[Path, str]]: ...
|
87
85
|
|
88
86
|
@abc.abstractmethod
|
89
|
-
def watch_entry(self) -> tuple[Path, Path]:
|
90
|
-
...
|
87
|
+
def watch_entry(self) -> tuple[Path, Path]: ...
|
91
88
|
|
92
89
|
@abc.abstractmethod
|
93
|
-
def top_level_paths(self) -> list[tuple[Path, PurePosixPath]]:
|
94
|
-
...
|
90
|
+
def top_level_paths(self) -> list[tuple[Path, PurePosixPath]]: ...
|
95
91
|
|
96
92
|
|
97
93
|
def _select_files(entries: list[_MountEntry]) -> list[tuple[Path, PurePosixPath]]:
|
modal/output.py
CHANGED
@@ -6,6 +6,7 @@ transitively importing Rich, as we do in global scope in _output.py. This allows
|
|
6
6
|
us to avoid importing Rich for client code that runs in the container environment.
|
7
7
|
|
8
8
|
"""
|
9
|
+
|
9
10
|
import contextlib
|
10
11
|
from collections.abc import Generator
|
11
12
|
from typing import TYPE_CHECKING, Optional
|