modal 0.67.1__py3-none-any.whl → 0.67.33__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/_clustered_functions.py +2 -2
- modal/_clustered_functions.pyi +2 -2
- modal/_container_entrypoint.py +8 -5
- modal/_output.py +29 -28
- modal/_pty.py +2 -2
- modal/_resolver.py +6 -5
- modal/_resources.py +3 -3
- modal/_runtime/asgi.py +46 -6
- modal/_runtime/container_io_manager.py +22 -26
- modal/_runtime/execution_context.py +2 -2
- modal/_runtime/telemetry.py +1 -2
- modal/_runtime/user_code_imports.py +12 -14
- modal/_serialization.py +3 -7
- modal/_traceback.py +5 -5
- modal/_tunnel.py +5 -4
- modal/_tunnel.pyi +2 -2
- modal/_utils/async_utils.py +53 -17
- modal/_utils/blob_utils.py +22 -7
- modal/_utils/function_utils.py +20 -10
- modal/_utils/grpc_testing.py +7 -6
- modal/_utils/grpc_utils.py +2 -3
- modal/_utils/hash_utils.py +2 -2
- modal/_utils/mount_utils.py +5 -4
- modal/_utils/package_utils.py +2 -3
- modal/_utils/pattern_matcher.py +6 -6
- modal/_utils/rand_pb_testing.py +3 -3
- modal/_utils/shell_utils.py +2 -1
- modal/_vendor/a2wsgi_wsgi.py +62 -72
- modal/_vendor/cloudpickle.py +1 -1
- modal/_watcher.py +8 -7
- modal/app.py +68 -62
- modal/app.pyi +104 -99
- modal/call_graph.py +6 -6
- modal/cli/_download.py +3 -2
- modal/cli/_traceback.py +4 -4
- modal/cli/app.py +4 -4
- modal/cli/container.py +4 -4
- modal/cli/dict.py +1 -1
- modal/cli/environment.py +2 -3
- modal/cli/import_refs.py +1 -1
- modal/cli/launch.py +2 -2
- modal/cli/network_file_system.py +1 -1
- modal/cli/profile.py +1 -1
- modal/cli/programs/run_jupyter.py +2 -2
- modal/cli/programs/vscode.py +3 -3
- modal/cli/queues.py +1 -1
- modal/cli/run.py +6 -6
- modal/cli/secret.py +3 -3
- modal/cli/utils.py +2 -1
- modal/cli/volume.py +3 -3
- modal/client.py +6 -11
- modal/client.pyi +18 -27
- modal/cloud_bucket_mount.py +3 -3
- modal/cloud_bucket_mount.pyi +2 -2
- modal/cls.py +100 -47
- modal/cls.pyi +40 -40
- modal/config.py +3 -2
- modal/container_process.py +6 -2
- modal/dict.py +6 -3
- modal/dict.pyi +10 -9
- modal/environments.py +3 -3
- modal/environments.pyi +3 -3
- modal/exception.py +2 -3
- modal/functions.py +112 -104
- modal/functions.pyi +77 -58
- modal/image.py +59 -57
- modal/image.pyi +104 -103
- modal/io_streams.py +20 -12
- modal/io_streams.pyi +24 -14
- modal/mount.py +24 -24
- modal/mount.pyi +28 -29
- modal/network_file_system.py +14 -11
- modal/network_file_system.pyi +12 -11
- modal/object.py +9 -8
- modal/object.pyi +47 -34
- modal/output.py +2 -1
- modal/parallel_map.py +4 -4
- modal/partial_function.py +10 -14
- modal/partial_function.pyi +17 -18
- modal/queue.py +11 -8
- modal/queue.pyi +23 -22
- modal/retries.py +38 -0
- modal/runner.py +8 -7
- modal/runner.pyi +8 -14
- modal/running_app.py +3 -3
- modal/sandbox.py +20 -13
- modal/sandbox.pyi +73 -72
- modal/scheduler_placement.py +2 -1
- modal/secret.py +7 -7
- modal/secret.pyi +12 -12
- modal/serving.py +4 -3
- modal/serving.pyi +5 -4
- modal/token_flow.py +3 -2
- modal/token_flow.pyi +3 -3
- modal/volume.py +16 -23
- modal/volume.pyi +17 -16
- {modal-0.67.1.dist-info → modal-0.67.33.dist-info}/METADATA +2 -2
- modal-0.67.33.dist-info/RECORD +168 -0
- modal_docs/mdmd/signatures.py +1 -2
- modal_global_objects/mounts/python_standalone.py +1 -1
- modal_proto/api.proto +15 -0
- modal_proto/api_grpc.py +32 -0
- modal_proto/api_pb2.py +674 -654
- modal_proto/api_pb2.pyi +45 -1
- 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/_version_generated.py +1 -1
- modal-0.67.1.dist-info/RECORD +0 -168
- {modal-0.67.1.dist-info → modal-0.67.33.dist-info}/LICENSE +0 -0
- {modal-0.67.1.dist-info → modal-0.67.33.dist-info}/WHEEL +0 -0
- {modal-0.67.1.dist-info → modal-0.67.33.dist-info}/entry_points.txt +0 -0
- {modal-0.67.1.dist-info → modal-0.67.33.dist-info}/top_level.txt +0 -0
modal/image.py
CHANGED
@@ -7,18 +7,15 @@ import shlex
|
|
7
7
|
import sys
|
8
8
|
import typing
|
9
9
|
import warnings
|
10
|
+
from collections.abc import Sequence
|
10
11
|
from dataclasses import dataclass
|
11
12
|
from inspect import isfunction
|
12
13
|
from pathlib import Path, PurePosixPath
|
13
14
|
from typing import (
|
14
15
|
Any,
|
15
16
|
Callable,
|
16
|
-
Dict,
|
17
|
-
List,
|
18
17
|
Literal,
|
19
18
|
Optional,
|
20
|
-
Sequence,
|
21
|
-
Set,
|
22
19
|
Union,
|
23
20
|
cast,
|
24
21
|
get_args,
|
@@ -59,7 +56,7 @@ ImageBuilderVersion = Literal["2023.12", "2024.04", "2024.10"]
|
|
59
56
|
# so that we fail fast / clearly in unsupported containers. Additionally, we enumerate the supported
|
60
57
|
# Python versions in mount.py where we specify the "standalone Python versions" we create mounts for.
|
61
58
|
# Consider consolidating these multiple sources of truth?
|
62
|
-
SUPPORTED_PYTHON_SERIES:
|
59
|
+
SUPPORTED_PYTHON_SERIES: dict[ImageBuilderVersion, list[str]] = {
|
63
60
|
"2024.10": ["3.9", "3.10", "3.11", "3.12", "3.13"],
|
64
61
|
"2024.04": ["3.9", "3.10", "3.11", "3.12"],
|
65
62
|
"2023.12": ["3.9", "3.10", "3.11", "3.12"],
|
@@ -74,7 +71,7 @@ def _validate_python_version(
|
|
74
71
|
) -> str:
|
75
72
|
if python_version is None:
|
76
73
|
# If Python version is unspecified, match the local version, up to the minor component
|
77
|
-
python_version = series_version = "{
|
74
|
+
python_version = series_version = "{}.{}".format(*sys.version_info)
|
78
75
|
elif not isinstance(python_version, str):
|
79
76
|
raise InvalidError(f"Python version must be specified as a string, not {type(python_version).__name__}")
|
80
77
|
elif not re.match(r"^3(?:\.\d{1,2}){1,2}(rc\d*)?$", python_version):
|
@@ -86,7 +83,7 @@ def _validate_python_version(
|
|
86
83
|
"Python version must be specified as 'major.minor' for this interface;"
|
87
84
|
f" micro-level specification ({python_version!r}) is not valid."
|
88
85
|
)
|
89
|
-
series_version = "{
|
86
|
+
series_version = "{}.{}".format(*components)
|
90
87
|
|
91
88
|
supported_series = SUPPORTED_PYTHON_SERIES[builder_version]
|
92
89
|
if series_version not in supported_series:
|
@@ -111,13 +108,13 @@ def _dockerhub_python_version(builder_version: ImageBuilderVersion, python_versi
|
|
111
108
|
# This allows us to publish one pre-built debian-slim image per Python series.
|
112
109
|
python_versions = _base_image_config("python", builder_version)
|
113
110
|
series_to_micro_version = dict(tuple(v.rsplit(".", 1)) for v in python_versions)
|
114
|
-
python_series_requested = "{
|
111
|
+
python_series_requested = "{}.{}".format(*version_components)
|
115
112
|
micro_version = series_to_micro_version[python_series_requested]
|
116
113
|
return f"{python_series_requested}.{micro_version}"
|
117
114
|
|
118
115
|
|
119
116
|
def _base_image_config(group: str, builder_version: ImageBuilderVersion) -> Any:
|
120
|
-
with open(LOCAL_REQUIREMENTS_DIR / "base-images.json"
|
117
|
+
with open(LOCAL_REQUIREMENTS_DIR / "base-images.json") as f:
|
121
118
|
data = json.load(f)
|
122
119
|
return data[group][builder_version]
|
123
120
|
|
@@ -146,7 +143,7 @@ def _get_modal_requirements_command(version: ImageBuilderVersion) -> str:
|
|
146
143
|
return f"{prefix} -r {CONTAINER_REQUIREMENTS_PATH}"
|
147
144
|
|
148
145
|
|
149
|
-
def _flatten_str_args(function_name: str, arg_name: str, args: Sequence[Union[str,
|
146
|
+
def _flatten_str_args(function_name: str, arg_name: str, args: Sequence[Union[str, list[str]]]) -> list[str]:
|
150
147
|
"""Takes a sequence of strings, or string lists, and flattens it.
|
151
148
|
|
152
149
|
Raises an error if any of the elements are not strings or string lists.
|
@@ -155,7 +152,7 @@ def _flatten_str_args(function_name: str, arg_name: str, args: Sequence[Union[st
|
|
155
152
|
def is_str_list(x):
|
156
153
|
return isinstance(x, list) and all(isinstance(y, str) for y in x)
|
157
154
|
|
158
|
-
ret:
|
155
|
+
ret: list[str] = []
|
159
156
|
for x in args:
|
160
157
|
if isinstance(x, str):
|
161
158
|
ret.append(x)
|
@@ -166,7 +163,7 @@ def _flatten_str_args(function_name: str, arg_name: str, args: Sequence[Union[st
|
|
166
163
|
return ret
|
167
164
|
|
168
165
|
|
169
|
-
def _validate_packages(packages:
|
166
|
+
def _validate_packages(packages: list[str]) -> bool:
|
170
167
|
"""Validates that a list of packages does not contain any command-line options."""
|
171
168
|
return not any(pkg.startswith("-") for pkg in packages)
|
172
169
|
|
@@ -219,7 +216,7 @@ def _get_image_builder_version(server_version: ImageBuilderVersion) -> ImageBuil
|
|
219
216
|
version_source = ""
|
220
217
|
version = server_version
|
221
218
|
|
222
|
-
supported_versions:
|
219
|
+
supported_versions: set[ImageBuilderVersion] = set(get_args(ImageBuilderVersion))
|
223
220
|
if version not in supported_versions:
|
224
221
|
if local_config_version is not None:
|
225
222
|
update_suggestion = "or remove your local configuration"
|
@@ -259,8 +256,8 @@ class _ImageRegistryConfig:
|
|
259
256
|
@dataclass
|
260
257
|
class DockerfileSpec:
|
261
258
|
# Ideally we would use field() with default_factory=, but doesn't work with synchronicity type-stub gen
|
262
|
-
commands:
|
263
|
-
context_files:
|
259
|
+
commands: list[str]
|
260
|
+
context_files: dict[str, str]
|
264
261
|
|
265
262
|
|
266
263
|
async def _image_await_build_result(image_id: str, client: _Client) -> api_pb2.ImageJoinStreamingResponse:
|
@@ -310,8 +307,8 @@ class _Image(_Object, type_prefix="im"):
|
|
310
307
|
"""
|
311
308
|
|
312
309
|
force_build: bool
|
313
|
-
inside_exceptions:
|
314
|
-
_serve_mounts:
|
310
|
+
inside_exceptions: list[Exception]
|
311
|
+
_serve_mounts: frozenset[_Mount] # used for mounts watching in `modal serve`
|
315
312
|
_deferred_mounts: Sequence[
|
316
313
|
_Mount
|
317
314
|
] # added as mounts on any container referencing the Image, see `def _mount_layers`
|
@@ -375,7 +372,7 @@ class _Image(_Object, type_prefix="im"):
|
|
375
372
|
"\n"
|
376
373
|
"Run `image.add_local_*` commands last in your image build to avoid rebuilding images with every local "
|
377
374
|
"file change. Modal will then add these files to containers on startup instead, saving build time.\n"
|
378
|
-
"If you need to run other build steps after adding local files, set `copy=True` to copy the files"
|
375
|
+
"If you need to run other build steps after adding local files, set `copy=True` to copy the files "
|
379
376
|
"directly into the image, at the expense of some added build time.\n"
|
380
377
|
"\n"
|
381
378
|
"Example:\n"
|
@@ -390,7 +387,7 @@ class _Image(_Object, type_prefix="im"):
|
|
390
387
|
@staticmethod
|
391
388
|
def _from_args(
|
392
389
|
*,
|
393
|
-
base_images: Optional[
|
390
|
+
base_images: Optional[dict[str, "_Image"]] = None,
|
394
391
|
dockerfile_function: Optional[Callable[[ImageBuilderVersion], DockerfileSpec]] = None,
|
395
392
|
secrets: Optional[Sequence[_Secret]] = None,
|
396
393
|
gpu_config: Optional[api_pb2.GPUConfig] = None,
|
@@ -680,13 +677,13 @@ class _Image(_Object, type_prefix="im"):
|
|
680
677
|
context_mount=mount,
|
681
678
|
)
|
682
679
|
|
683
|
-
def
|
684
|
-
"""Adds Python
|
680
|
+
def add_local_python_source(self, *modules: str, copy: bool = False) -> "_Image":
|
681
|
+
"""Adds locally available Python packages/modules to containers
|
685
682
|
|
686
|
-
Adds all files from the specified Python
|
683
|
+
Adds all files from the specified Python package or module to containers running the Image.
|
687
684
|
|
688
685
|
Packages are added to the `/root` directory of containers, which is on the `PYTHONPATH`
|
689
|
-
of any executed Modal Functions.
|
686
|
+
of any executed Modal Functions, enabling import of the module by that name.
|
690
687
|
|
691
688
|
By default (`copy=False`), the files are added to containers on startup and are not built into the actual Image,
|
692
689
|
which speeds up deployment.
|
@@ -696,9 +693,14 @@ class _Image(_Object, type_prefix="im"):
|
|
696
693
|
required if you want to run additional build steps after this one.
|
697
694
|
|
698
695
|
**Note:** This excludes all dot-prefixed subdirectories or files and all `.pyc`/`__pycache__` files.
|
699
|
-
To add full directories with finer control, use `.add_local_dir()` instead
|
696
|
+
To add full directories with finer control, use `.add_local_dir()` instead and specify `/root` as
|
697
|
+
the destination directory.
|
700
698
|
"""
|
701
|
-
|
699
|
+
|
700
|
+
def only_py_files(filename):
|
701
|
+
return filename.endswith(".py")
|
702
|
+
|
703
|
+
mount = _Mount.from_local_python_packages(*modules, condition=only_py_files)
|
702
704
|
return self._add_mount_layer_or_copy(mount, copy=copy)
|
703
705
|
|
704
706
|
def copy_local_dir(self, local_path: Union[str, Path], remote_path: Union[str, Path] = ".") -> "_Image":
|
@@ -720,7 +722,7 @@ class _Image(_Object, type_prefix="im"):
|
|
720
722
|
|
721
723
|
def pip_install(
|
722
724
|
self,
|
723
|
-
*packages: Union[str,
|
725
|
+
*packages: Union[str, list[str]], # A list of Python packages, eg. ["numpy", "matplotlib>=3.5.0"]
|
724
726
|
find_links: Optional[str] = None, # Passes -f (--find-links) pip install
|
725
727
|
index_url: Optional[str] = None, # Passes -i (--index-url) to pip install
|
726
728
|
extra_index_url: Optional[str] = None, # Passes --extra-index-url to pip install
|
@@ -762,7 +764,7 @@ class _Image(_Object, type_prefix="im"):
|
|
762
764
|
return self
|
763
765
|
|
764
766
|
def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
|
765
|
-
package_args =
|
767
|
+
package_args = shlex.join(sorted(pkgs))
|
766
768
|
extra_args = _make_pip_install_args(find_links, index_url, extra_index_url, pre, extra_options)
|
767
769
|
commands = ["FROM base", f"RUN python -m pip install {package_args} {extra_args}"]
|
768
770
|
if not _validate_packages(pkgs):
|
@@ -924,7 +926,7 @@ class _Image(_Object, type_prefix="im"):
|
|
924
926
|
def pip_install_from_pyproject(
|
925
927
|
self,
|
926
928
|
pyproject_toml: str,
|
927
|
-
optional_dependencies:
|
929
|
+
optional_dependencies: list[str] = [],
|
928
930
|
*,
|
929
931
|
find_links: Optional[str] = None, # Passes -f (--find-links) pip install
|
930
932
|
index_url: Optional[str] = None, # Passes -i (--index-url) to pip install
|
@@ -968,7 +970,7 @@ class _Image(_Object, type_prefix="im"):
|
|
968
970
|
dependencies.extend(optionals[dep_group_name])
|
969
971
|
|
970
972
|
extra_args = _make_pip_install_args(find_links, index_url, extra_index_url, pre, extra_options)
|
971
|
-
package_args =
|
973
|
+
package_args = shlex.join(sorted(dependencies))
|
972
974
|
commands = ["FROM base", f"RUN python -m pip install {package_args} {extra_args}"]
|
973
975
|
if version > "2023.12": # Back-compat for legacy trailing space
|
974
976
|
commands = [cmd.strip() for cmd in commands]
|
@@ -994,11 +996,11 @@ class _Image(_Object, type_prefix="im"):
|
|
994
996
|
old_installer: bool = False,
|
995
997
|
force_build: bool = False, # Ignore cached builds, similar to 'docker build --no-cache'
|
996
998
|
# Selected optional dependency groups to install (See https://python-poetry.org/docs/cli/#install)
|
997
|
-
with_:
|
999
|
+
with_: list[str] = [],
|
998
1000
|
# Selected optional dependency groups to exclude (See https://python-poetry.org/docs/cli/#install)
|
999
|
-
without:
|
1001
|
+
without: list[str] = [],
|
1000
1002
|
# Only install dependency groups specifed in this list.
|
1001
|
-
only:
|
1003
|
+
only: list[str] = [],
|
1002
1004
|
*,
|
1003
1005
|
secrets: Sequence[_Secret] = [],
|
1004
1006
|
gpu: GPU_T = None,
|
@@ -1008,8 +1010,8 @@ class _Image(_Object, type_prefix="im"):
|
|
1008
1010
|
If not provided as argument the path to the lockfile is inferred. However, the
|
1009
1011
|
file has to exist, unless `ignore_lockfile` is set to `True`.
|
1010
1012
|
|
1011
|
-
Note that the root project of the poetry project is not installed,
|
1012
|
-
|
1013
|
+
Note that the root project of the poetry project is not installed, only the dependencies.
|
1014
|
+
For including local python source files see `add_local_python_source`
|
1013
1015
|
"""
|
1014
1016
|
|
1015
1017
|
def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
|
@@ -1066,8 +1068,8 @@ class _Image(_Object, type_prefix="im"):
|
|
1066
1068
|
|
1067
1069
|
def dockerfile_commands(
|
1068
1070
|
self,
|
1069
|
-
*dockerfile_commands: Union[str,
|
1070
|
-
context_files:
|
1071
|
+
*dockerfile_commands: Union[str, list[str]],
|
1072
|
+
context_files: dict[str, str] = {},
|
1071
1073
|
secrets: Sequence[_Secret] = [],
|
1072
1074
|
gpu: GPU_T = None,
|
1073
1075
|
# modal.Mount with local files to supply as build context for COPY commands
|
@@ -1093,7 +1095,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1093
1095
|
|
1094
1096
|
def entrypoint(
|
1095
1097
|
self,
|
1096
|
-
entrypoint_commands:
|
1098
|
+
entrypoint_commands: list[str],
|
1097
1099
|
) -> "_Image":
|
1098
1100
|
"""Set the entrypoint for the image."""
|
1099
1101
|
args_str = _flatten_str_args("entrypoint", "entrypoint_files", entrypoint_commands)
|
@@ -1104,7 +1106,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1104
1106
|
|
1105
1107
|
def shell(
|
1106
1108
|
self,
|
1107
|
-
shell_commands:
|
1109
|
+
shell_commands: list[str],
|
1108
1110
|
) -> "_Image":
|
1109
1111
|
"""Overwrite default shell for the image."""
|
1110
1112
|
args_str = _flatten_str_args("shell", "shell_commands", shell_commands)
|
@@ -1115,7 +1117,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1115
1117
|
|
1116
1118
|
def run_commands(
|
1117
1119
|
self,
|
1118
|
-
*commands: Union[str,
|
1120
|
+
*commands: Union[str, list[str]],
|
1119
1121
|
secrets: Sequence[_Secret] = [],
|
1120
1122
|
gpu: GPU_T = None,
|
1121
1123
|
force_build: bool = False, # Ignore cached builds, similar to 'docker build --no-cache'
|
@@ -1147,8 +1149,8 @@ class _Image(_Object, type_prefix="im"):
|
|
1147
1149
|
|
1148
1150
|
def conda_install(
|
1149
1151
|
self,
|
1150
|
-
*packages: Union[str,
|
1151
|
-
channels:
|
1152
|
+
*packages: Union[str, list[str]], # A list of Python packages, eg. ["numpy", "matplotlib>=3.5.0"]
|
1153
|
+
channels: list[str] = [], # A list of Conda channels, eg. ["conda-forge", "nvidia"]
|
1152
1154
|
force_build: bool = False, # Ignore cached builds, similar to 'docker build --no-cache'
|
1153
1155
|
secrets: Sequence[_Secret] = [],
|
1154
1156
|
gpu: GPU_T = None,
|
@@ -1208,11 +1210,11 @@ class _Image(_Object, type_prefix="im"):
|
|
1208
1210
|
def micromamba_install(
|
1209
1211
|
self,
|
1210
1212
|
# A list of Python packages, eg. ["numpy", "matplotlib>=3.5.0"]
|
1211
|
-
*packages: Union[str,
|
1213
|
+
*packages: Union[str, list[str]],
|
1212
1214
|
# A local path to a file containing package specifications
|
1213
1215
|
spec_file: Optional[str] = None,
|
1214
1216
|
# A list of Conda channels, eg. ["conda-forge", "nvidia"].
|
1215
|
-
channels:
|
1217
|
+
channels: list[str] = [],
|
1216
1218
|
force_build: bool = False, # Ignore cached builds, similar to 'docker build --no-cache'
|
1217
1219
|
secrets: Sequence[_Secret] = [],
|
1218
1220
|
gpu: GPU_T = None,
|
@@ -1223,7 +1225,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1223
1225
|
return self
|
1224
1226
|
|
1225
1227
|
def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
|
1226
|
-
package_args =
|
1228
|
+
package_args = shlex.join(pkgs)
|
1227
1229
|
channel_args = "".join(f" -c {channel}" for channel in channels)
|
1228
1230
|
|
1229
1231
|
space = " " if package_args else ""
|
@@ -1251,10 +1253,10 @@ class _Image(_Object, type_prefix="im"):
|
|
1251
1253
|
def _registry_setup_commands(
|
1252
1254
|
tag: str,
|
1253
1255
|
builder_version: ImageBuilderVersion,
|
1254
|
-
setup_commands:
|
1256
|
+
setup_commands: list[str],
|
1255
1257
|
add_python: Optional[str] = None,
|
1256
|
-
) ->
|
1257
|
-
add_python_commands:
|
1258
|
+
) -> list[str]:
|
1259
|
+
add_python_commands: list[str] = []
|
1258
1260
|
if add_python:
|
1259
1261
|
_validate_python_version(add_python, builder_version, allow_micro_granularity=False)
|
1260
1262
|
add_python_commands = [
|
@@ -1285,7 +1287,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1285
1287
|
tag: str,
|
1286
1288
|
*,
|
1287
1289
|
secret: Optional[_Secret] = None,
|
1288
|
-
setup_dockerfile_commands:
|
1290
|
+
setup_dockerfile_commands: list[str] = [],
|
1289
1291
|
force_build: bool = False, # Ignore cached builds, similar to 'docker build --no-cache'
|
1290
1292
|
add_python: Optional[str] = None,
|
1291
1293
|
**kwargs,
|
@@ -1344,7 +1346,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1344
1346
|
tag: str,
|
1345
1347
|
secret: Optional[_Secret] = None,
|
1346
1348
|
*,
|
1347
|
-
setup_dockerfile_commands:
|
1349
|
+
setup_dockerfile_commands: list[str] = [],
|
1348
1350
|
force_build: bool = False, # Ignore cached builds, similar to 'docker build --no-cache'
|
1349
1351
|
add_python: Optional[str] = None,
|
1350
1352
|
**kwargs,
|
@@ -1395,7 +1397,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1395
1397
|
tag: str,
|
1396
1398
|
secret: Optional[_Secret] = None,
|
1397
1399
|
*,
|
1398
|
-
setup_dockerfile_commands:
|
1400
|
+
setup_dockerfile_commands: list[str] = [],
|
1399
1401
|
force_build: bool = False, # Ignore cached builds, similar to 'docker build --no-cache'
|
1400
1402
|
add_python: Optional[str] = None,
|
1401
1403
|
**kwargs,
|
@@ -1550,7 +1552,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1550
1552
|
|
1551
1553
|
def apt_install(
|
1552
1554
|
self,
|
1553
|
-
*packages: Union[str,
|
1555
|
+
*packages: Union[str, list[str]], # A list of packages, e.g. ["ssh", "libpq-dev"]
|
1554
1556
|
force_build: bool = False, # Ignore cached builds, similar to 'docker build --no-cache'
|
1555
1557
|
secrets: Sequence[_Secret] = [],
|
1556
1558
|
gpu: GPU_T = None,
|
@@ -1567,7 +1569,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1567
1569
|
if not pkgs:
|
1568
1570
|
return self
|
1569
1571
|
|
1570
|
-
package_args =
|
1572
|
+
package_args = shlex.join(pkgs)
|
1571
1573
|
|
1572
1574
|
def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
|
1573
1575
|
commands = [
|
@@ -1590,11 +1592,11 @@ class _Image(_Object, type_prefix="im"):
|
|
1590
1592
|
raw_f: Callable[..., Any],
|
1591
1593
|
secrets: Sequence[_Secret] = (), # Optional Modal Secret objects with environment variables for the container
|
1592
1594
|
gpu: Union[
|
1593
|
-
GPU_T,
|
1595
|
+
GPU_T, list[GPU_T]
|
1594
1596
|
] = None, # GPU request as string ("any", "T4", ...), object (`modal.GPU.A100()`, ...), or a list of either
|
1595
1597
|
mounts: Sequence[_Mount] = (), # Mounts attached to the function
|
1596
|
-
volumes:
|
1597
|
-
network_file_systems:
|
1598
|
+
volumes: dict[Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]] = {}, # Volume mount paths
|
1599
|
+
network_file_systems: dict[Union[str, PurePosixPath], _NetworkFileSystem] = {}, # NFS mount paths
|
1598
1600
|
cpu: Optional[float] = None, # How many CPU cores to request. This is a soft limit.
|
1599
1601
|
memory: Optional[int] = None, # How much memory to request, in MiB. This is a soft limit.
|
1600
1602
|
timeout: Optional[int] = 60 * 60, # Maximum execution time of the function in seconds.
|
@@ -1602,7 +1604,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1602
1604
|
cloud: Optional[str] = None, # Cloud provider to run the function on. Possible values are aws, gcp, oci, auto.
|
1603
1605
|
region: Optional[Union[str, Sequence[str]]] = None, # Region or regions to run the function on.
|
1604
1606
|
args: Sequence[Any] = (), # Positional arguments to the function.
|
1605
|
-
kwargs:
|
1607
|
+
kwargs: dict[str, Any] = {}, # Keyword arguments to the function.
|
1606
1608
|
) -> "_Image":
|
1607
1609
|
"""Run user-defined function `raw_f` as an image build step. The function runs just like an ordinary Modal
|
1608
1610
|
function, and any kwargs accepted by `@app.function` (such as `Mount`s, `NetworkFileSystem`s,
|
@@ -1676,7 +1678,7 @@ class _Image(_Object, type_prefix="im"):
|
|
1676
1678
|
force_build=self.force_build or force_build,
|
1677
1679
|
)
|
1678
1680
|
|
1679
|
-
def env(self, vars:
|
1681
|
+
def env(self, vars: dict[str, str]) -> "_Image":
|
1680
1682
|
"""Sets the environment variables in an Image.
|
1681
1683
|
|
1682
1684
|
**Example**
|