modAL 1.0.0__tar.gz → 1.0.0.dev1__tar.gz
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-1.0.0 → modal-1.0.0.dev1}/PKG-INFO +1 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/__init__.py +4 -10
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_functions.py +90 -15
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_object.py +14 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_partial_function.py +14 -4
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_serialization.py +2 -2
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/function_utils.py +6 -3
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/grpc_utils.py +1 -6
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/app.py +104 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/app.pyi +127 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/app.py +19 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/programs/run_jupyter.py +1 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/programs/vscode.py +1 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/client.pyi +10 -2
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cls.py +14 -9
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cls.pyi +17 -2
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/config.py +16 -5
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/container_process.py +9 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/container_process.pyi +3 -3
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/dict.py +5 -3
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/environments.py +3 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/exception.py +1 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/functions.pyi +23 -2
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/image.py +36 -12
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/image.pyi +5 -2
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/mount.py +65 -2
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/mount.pyi +1 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/network_file_system.py +5 -3
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/object.pyi +6 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/queue.py +5 -3
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/runner.py +19 -2
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/runner.pyi +5 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/sandbox.py +32 -78
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/sandbox.pyi +7 -102
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/secret.py +3 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/serving.py +6 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/serving.pyi +3 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/volume.py +17 -8
- {modal-1.0.0 → modal-1.0.0.dev1}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/api.proto +2 -29
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/api_grpc.py +0 -32
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/api_pb2.py +756 -788
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/api_pb2.pyi +12 -86
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/api_pb2_grpc.py +0 -66
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/api_pb2_grpc.pyi +0 -20
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/modal_api_grpc.py +0 -2
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_version/__init__.py +1 -1
- {modal-1.0.0 → modal-1.0.0.dev1}/LICENSE +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/README.md +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/__main__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_clustered_functions.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_clustered_functions.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_container_entrypoint.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_ipython.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_location.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_output.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_pty.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_resolver.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_resources.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_runtime/__init__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_runtime/asgi.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_runtime/execution_context.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_runtime/telemetry.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_traceback.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_tunnel.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_tunnel.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_type_manager.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/__init__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/app_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/async_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/blob_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/deprecation.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/docker_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/git_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/hash_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/http_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/logger.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/mount_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/name_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/package_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_utils/shell_utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_vendor/__init__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_vendor/tblib.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/_watcher.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/call_graph.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/__init__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/_download.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/_traceback.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/cluster.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/config.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/container.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/dict.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/entry_point.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/environment.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/import_refs.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/launch.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/network_file_system.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/profile.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/programs/__init__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/queues.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/run.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/secret.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/token.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/utils.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cli/volume.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/client.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/dict.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/environments.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/experimental/__init__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/experimental/ipython.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/file_io.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/file_io.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/file_pattern_matcher.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/functions.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/gpu.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/io_streams.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/io_streams.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/network_file_system.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/object.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/output.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/parallel_map.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/parallel_map.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/partial_function.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/partial_function.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/proxy.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/proxy.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/py.typed +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/queue.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/requirements/2023.12.312.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/requirements/2023.12.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/requirements/2024.04.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/requirements/2024.10.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/requirements/PREVIEW.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/requirements/README.md +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/requirements/base-images.json +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/retries.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/running_app.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/schedule.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/scheduler_placement.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/secret.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/snapshot.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/snapshot.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/stream_type.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/token_flow.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/token_flow.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal/volume.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal.egg-info/requires.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal.egg-info/top_level.txt +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_docs/__init__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/__init__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/modal_options_grpc.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/options.proto +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/options_grpc.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/options_pb2.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/options_pb2.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/options_pb2_grpc.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/options_pb2_grpc.pyi +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_proto/py.typed +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/modal_version/__main__.py +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/pyproject.toml +0 -0
- {modal-1.0.0 → modal-1.0.0.dev1}/setup.cfg +0 -0
|
@@ -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, Stub
|
|
15
15
|
from .client import Client
|
|
16
16
|
from .cloud_bucket_mount import CloudBucketMount
|
|
17
17
|
from .cls import Cls, parameter
|
|
@@ -20,6 +20,7 @@ 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
|
|
23
24
|
from .network_file_system import NetworkFileSystem
|
|
24
25
|
from .output import enable_output
|
|
25
26
|
from .partial_function import (
|
|
@@ -53,7 +54,6 @@ except Exception:
|
|
|
53
54
|
print()
|
|
54
55
|
raise
|
|
55
56
|
|
|
56
|
-
|
|
57
57
|
__all__ = [
|
|
58
58
|
"__version__",
|
|
59
59
|
"App",
|
|
@@ -66,6 +66,7 @@ __all__ = [
|
|
|
66
66
|
"Function",
|
|
67
67
|
"FunctionCall",
|
|
68
68
|
"Image",
|
|
69
|
+
"Mount",
|
|
69
70
|
"NetworkFileSystem",
|
|
70
71
|
"Period",
|
|
71
72
|
"Proxy",
|
|
@@ -76,6 +77,7 @@ __all__ = [
|
|
|
76
77
|
"SandboxSnapshot",
|
|
77
78
|
"SchedulerPlacement",
|
|
78
79
|
"Secret",
|
|
80
|
+
"Stub",
|
|
79
81
|
"Tunnel",
|
|
80
82
|
"Volume",
|
|
81
83
|
"asgi_app",
|
|
@@ -97,11 +99,3 @@ __all__ = [
|
|
|
97
99
|
"web_server",
|
|
98
100
|
"wsgi_app",
|
|
99
101
|
]
|
|
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}'")
|
|
@@ -6,7 +6,7 @@ import textwrap
|
|
|
6
6
|
import time
|
|
7
7
|
import typing
|
|
8
8
|
import warnings
|
|
9
|
-
from collections.abc import AsyncGenerator, Sequence, Sized
|
|
9
|
+
from collections.abc import AsyncGenerator, Collection, 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 deprecation_warning
|
|
44
|
+
from ._utils.deprecation import deprecation_error, deprecation_warning, renamed_parameter
|
|
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, get_sys_modules_mounts
|
|
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,6 +405,12 @@ 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
|
+
|
|
408
414
|
|
|
409
415
|
def _parse_retries(
|
|
410
416
|
retries: Optional[Union[int, Retries]],
|
|
@@ -505,6 +511,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
505
511
|
is_generator: bool = False,
|
|
506
512
|
gpu: Union[GPU_T, list[GPU_T]] = None,
|
|
507
513
|
# TODO: maybe break this out into a separate decorator for notebooks.
|
|
514
|
+
mounts: Collection[_Mount] = (),
|
|
508
515
|
network_file_systems: dict[Union[str, PurePosixPath], _NetworkFileSystem] = {},
|
|
509
516
|
volumes: dict[Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]] = {},
|
|
510
517
|
webhook_config: Optional[api_pb2.WebhookConfig] = None,
|
|
@@ -560,6 +567,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
560
567
|
assert not webhook_config
|
|
561
568
|
assert not schedule
|
|
562
569
|
|
|
570
|
+
explicit_mounts = mounts
|
|
571
|
+
|
|
563
572
|
include_source_mode = get_include_source_mode(include_source)
|
|
564
573
|
if include_source_mode != IncludeSourceMode.INCLUDE_NOTHING:
|
|
565
574
|
entrypoint_mounts = info.get_entrypoint_mount()
|
|
@@ -568,9 +577,34 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
568
577
|
|
|
569
578
|
all_mounts = [
|
|
570
579
|
_get_client_mount(),
|
|
580
|
+
*explicit_mounts,
|
|
571
581
|
*entrypoint_mounts.values(),
|
|
572
582
|
]
|
|
573
583
|
|
|
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
|
+
|
|
574
608
|
retry_policy = _parse_retries(
|
|
575
609
|
retries, f"Function '{info.get_tag()}'" if info.raw_f else f"Class '{info.get_tag()}'"
|
|
576
610
|
)
|
|
@@ -607,6 +641,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
607
641
|
image=image,
|
|
608
642
|
secrets=secrets,
|
|
609
643
|
gpu=gpu,
|
|
644
|
+
mounts=mounts,
|
|
610
645
|
network_file_systems=network_file_systems,
|
|
611
646
|
volumes=volumes,
|
|
612
647
|
memory=memory,
|
|
@@ -714,7 +749,16 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
714
749
|
|
|
715
750
|
def _deps(only_explicit_mounts=False) -> list[_Object]:
|
|
716
751
|
deps: list[_Object] = list(secrets)
|
|
717
|
-
if
|
|
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:
|
|
718
762
|
deps += list(all_mounts)
|
|
719
763
|
if proxy:
|
|
720
764
|
deps.append(proxy)
|
|
@@ -982,6 +1026,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
982
1026
|
obj._build_args = dict( # See get_build_def
|
|
983
1027
|
secrets=repr(secrets),
|
|
984
1028
|
gpu_config=repr([parse_gpu_config(_gpu) for _gpu in gpus]),
|
|
1029
|
+
mounts=repr(mounts),
|
|
985
1030
|
network_file_systems=repr(network_file_systems),
|
|
986
1031
|
)
|
|
987
1032
|
# these key are excluded if empty to avoid rebuilds on client upgrade
|
|
@@ -1145,8 +1190,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1145
1190
|
|
|
1146
1191
|
@live_method
|
|
1147
1192
|
async def keep_warm(self, warm_pool_size: int) -> None:
|
|
1148
|
-
"""
|
|
1149
|
-
Set the warm pool size for the Function.
|
|
1193
|
+
"""Set the warm pool size for the Function.
|
|
1150
1194
|
|
|
1151
1195
|
DEPRECATED: Please adapt your code to use the more general `update_autoscaler` method instead:
|
|
1152
1196
|
|
|
@@ -1211,6 +1255,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1211
1255
|
return cls._from_loader(_load_remote, rep, is_another_app=True, hydrate_lazily=True)
|
|
1212
1256
|
|
|
1213
1257
|
@classmethod
|
|
1258
|
+
@renamed_parameter((2024, 12, 18), "tag", "name")
|
|
1214
1259
|
def from_name(
|
|
1215
1260
|
cls: type["_Function"],
|
|
1216
1261
|
app_name: str,
|
|
@@ -1243,6 +1288,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1243
1288
|
return cls._from_name(app_name, name, namespace, environment_name)
|
|
1244
1289
|
|
|
1245
1290
|
@staticmethod
|
|
1291
|
+
@renamed_parameter((2024, 12, 18), "tag", "name")
|
|
1246
1292
|
async def lookup(
|
|
1247
1293
|
app_name: str,
|
|
1248
1294
|
name: str,
|
|
@@ -1250,8 +1296,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1250
1296
|
client: Optional[_Client] = None,
|
|
1251
1297
|
environment_name: Optional[str] = None,
|
|
1252
1298
|
) -> "_Function":
|
|
1253
|
-
"""
|
|
1254
|
-
Lookup a Function from a deployed App by its name.
|
|
1299
|
+
"""Lookup a Function from a deployed App by its name.
|
|
1255
1300
|
|
|
1256
1301
|
DEPRECATED: This method is deprecated in favor of `modal.Function.from_name`.
|
|
1257
1302
|
|
|
@@ -1375,8 +1420,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
|
1375
1420
|
@property
|
|
1376
1421
|
@live_method
|
|
1377
1422
|
async def web_url(self) -> Optional[str]:
|
|
1378
|
-
"""
|
|
1379
|
-
Deprecated. Use the `Function.get_web_url()` method instead.
|
|
1423
|
+
"""Deprecated. Use the `Function.get_web_url()` method instead.
|
|
1380
1424
|
|
|
1381
1425
|
URL of a Function running as a web endpoint.
|
|
1382
1426
|
"""
|
|
@@ -1493,6 +1537,20 @@ Use the `Function.get_web_url()` method instead.
|
|
|
1493
1537
|
async for res in invocation.run_generator():
|
|
1494
1538
|
yield res
|
|
1495
1539
|
|
|
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
|
+
|
|
1496
1554
|
@synchronizer.no_io_translation
|
|
1497
1555
|
@live_method
|
|
1498
1556
|
async def remote(self, *args: P.args, **kwargs: P.kwargs) -> ReturnType:
|
|
@@ -1605,7 +1663,7 @@ Use the `Function.get_web_url()` method instead.
|
|
|
1605
1663
|
"""
|
|
1606
1664
|
self._check_no_web_url("_experimental_spawn")
|
|
1607
1665
|
if self._is_generator:
|
|
1608
|
-
|
|
1666
|
+
invocation = await self._call_generator_nowait(args, kwargs)
|
|
1609
1667
|
else:
|
|
1610
1668
|
invocation = await self._call_function_nowait(
|
|
1611
1669
|
args, kwargs, function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_ASYNC
|
|
@@ -1637,13 +1695,14 @@ Use the `Function.get_web_url()` method instead.
|
|
|
1637
1695
|
"""
|
|
1638
1696
|
self._check_no_web_url("spawn")
|
|
1639
1697
|
if self._is_generator:
|
|
1640
|
-
|
|
1698
|
+
invocation = await self._call_generator_nowait(args, kwargs)
|
|
1641
1699
|
else:
|
|
1642
1700
|
invocation = await self._call_function_nowait(args, kwargs, api_pb2.FUNCTION_CALL_INVOCATION_TYPE_ASYNC)
|
|
1643
1701
|
|
|
1644
1702
|
fc: _FunctionCall[ReturnType] = _FunctionCall._new_hydrated(
|
|
1645
1703
|
invocation.function_call_id, invocation.client, None
|
|
1646
1704
|
)
|
|
1705
|
+
fc._is_generator = self._is_generator if self._is_generator else False
|
|
1647
1706
|
return fc
|
|
1648
1707
|
|
|
1649
1708
|
def get_raw_f(self) -> Callable[..., Any]:
|
|
@@ -1702,8 +1761,22 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1702
1761
|
|
|
1703
1762
|
The returned coroutine is not cancellation-safe.
|
|
1704
1763
|
"""
|
|
1764
|
+
|
|
1765
|
+
if self._is_generator:
|
|
1766
|
+
raise Exception("Cannot get the result of a generator function call. Use `get_gen` instead.")
|
|
1767
|
+
|
|
1705
1768
|
return await self._invocation().poll_function(timeout=timeout)
|
|
1706
1769
|
|
|
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
|
+
|
|
1707
1780
|
async def get_call_graph(self) -> list[InputInfo]:
|
|
1708
1781
|
"""Returns a structure representing the call graph from a given root
|
|
1709
1782
|
call ID, along with the status of execution for each node.
|
|
@@ -1734,7 +1807,9 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1734
1807
|
await retry_transient_errors(self._client.stub.FunctionCallCancel, request)
|
|
1735
1808
|
|
|
1736
1809
|
@staticmethod
|
|
1737
|
-
async def from_id(
|
|
1810
|
+
async def from_id(
|
|
1811
|
+
function_call_id: str, client: Optional[_Client] = None, is_generator: bool = False
|
|
1812
|
+
) -> "_FunctionCall[Any]":
|
|
1738
1813
|
"""Instantiate a FunctionCall object from an existing ID.
|
|
1739
1814
|
|
|
1740
1815
|
Examples:
|
|
@@ -1757,6 +1832,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1757
1832
|
client = await _Client.from_env()
|
|
1758
1833
|
|
|
1759
1834
|
fc: _FunctionCall[Any] = _FunctionCall._new_hydrated(function_call_id, client, None)
|
|
1835
|
+
fc._is_generator = is_generator
|
|
1760
1836
|
return fc
|
|
1761
1837
|
|
|
1762
1838
|
@staticmethod
|
|
@@ -1787,8 +1863,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
|
|
|
1787
1863
|
|
|
1788
1864
|
|
|
1789
1865
|
async def _gather(*function_calls: _FunctionCall[T]) -> typing.Sequence[T]:
|
|
1790
|
-
"""
|
|
1791
|
-
Deprecated: Please use `modal.FunctionCall.gather()` instead."""
|
|
1866
|
+
"""Deprecated: Please use `modal.FunctionCall.gather()` instead."""
|
|
1792
1867
|
deprecation_warning(
|
|
1793
1868
|
(2025, 2, 24),
|
|
1794
1869
|
"`modal.functions.gather()` is deprecated; please use `modal.FunctionCall.gather()` instead.",
|
|
@@ -10,6 +10,7 @@ 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
|
|
13
14
|
from .client import _Client
|
|
14
15
|
from .config import config, logger
|
|
15
16
|
from .exception import ExecutionError, InvalidError
|
|
@@ -236,6 +237,19 @@ class _Object:
|
|
|
236
237
|
|
|
237
238
|
return self._deps if self._deps is not None else default_deps
|
|
238
239
|
|
|
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
|
+
|
|
239
253
|
async def hydrate(self, client: Optional[_Client] = None) -> Self:
|
|
240
254
|
"""Synchronize the local object with its identity on the Modal server.
|
|
241
255
|
|
|
@@ -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 callable_has_non_self_params
|
|
21
|
+
from ._utils.function_utils import callable_has_non_self_non_default_params, callable_has_non_self_params
|
|
22
22
|
from .config import logger
|
|
23
23
|
from .exception import InvalidError
|
|
24
24
|
|
|
@@ -181,11 +181,21 @@ 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"
|
|
184
|
+
raise InvalidError(f"`@modal.{decorator_name}` 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
|
-
|
|
188
|
+
if callable_has_non_self_non_default_params(self.raw_f):
|
|
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
|
+
)
|
|
189
199
|
|
|
190
200
|
def _get_raw_f(self) -> Callable[P, ReturnType]:
|
|
191
201
|
assert self.raw_f is not None
|
|
@@ -649,7 +659,7 @@ def _web_server(
|
|
|
649
659
|
def _build(
|
|
650
660
|
_warn_parentheses_missing=None, *, force: bool = False, timeout: int = 86400
|
|
651
661
|
) -> Callable[[Union[_PartialFunction, NullaryMethod]], _PartialFunction]:
|
|
652
|
-
"""
|
|
662
|
+
"""
|
|
653
663
|
Decorator for methods that execute at _build time_ to create a new Image layer.
|
|
654
664
|
|
|
655
665
|
**Deprecated**: This function is deprecated. We recommend using `modal.Volume`
|
|
@@ -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
|
-
|
|
328
|
-
|
|
327
|
+
"bytes": asgi.websocket_send.bytes if asgi.websocket_send.HasField("bytes") else None,
|
|
328
|
+
"text": asgi.websocket_send.text if asgi.websocket_send.HasField("text") else None,
|
|
329
329
|
}
|
|
330
330
|
elif msg_type == "websocket_disconnect":
|
|
331
331
|
return {
|
|
@@ -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 logger
|
|
26
|
+
from ..config import config, logger
|
|
27
27
|
from ..exception import (
|
|
28
28
|
DeserializationError,
|
|
29
29
|
ExecutionError,
|
|
@@ -627,7 +627,8 @@ 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 #
|
|
630
|
+
INCLUDE_MAIN_PACKAGE = True # also represented by AUTOMOUNT=0 in config
|
|
631
|
+
INCLUDE_FIRST_PARTY = "legacy" # mounts all "local" modules in sys.modules - represented by AUTOMOUNT=1 in config
|
|
631
632
|
|
|
632
633
|
|
|
633
634
|
def get_include_source_mode(function_or_app_specific) -> IncludeSourceMode:
|
|
@@ -649,4 +650,6 @@ def get_include_source_mode(function_or_app_specific) -> IncludeSourceMode:
|
|
|
649
650
|
# explicitly set in app/function
|
|
650
651
|
return IncludeSourceMode(function_or_app_specific)
|
|
651
652
|
|
|
652
|
-
|
|
653
|
+
# note that the automount config boolean isn't a 1-1 mapping with include_source!
|
|
654
|
+
legacy_automount_mode: bool = config.get("automount")
|
|
655
|
+
return IncludeSourceMode.INCLUDE_FIRST_PARTY if legacy_automount_mode else IncludeSourceMode.INCLUDE_MAIN_PACKAGE
|
|
@@ -148,20 +148,15 @@ 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_trailing_metadata(trailing_metadata: grpclib.events.RecvTrailingMetadata) -> 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:
|
|
160
156
|
if token := trailing_metadata.metadata.get("x-modal-auth-token"):
|
|
161
157
|
metadata["x-modal-auth-token"] = str(token)
|
|
162
158
|
|
|
163
159
|
grpclib.events.listen(channel, grpclib.events.SendRequest, send_request)
|
|
164
|
-
grpclib.events.listen(channel, grpclib.events.RecvInitialMetadata, recv_initial_metadata)
|
|
165
160
|
grpclib.events.listen(channel, grpclib.events.RecvTrailingMetadata, recv_trailing_metadata)
|
|
166
161
|
|
|
167
162
|
return channel
|