modal 1.0.3.dev14__tar.gz → 1.0.3.dev21__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.3.dev14 → modal-1.0.3.dev21}/PKG-INFO +1 -1
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/run.py +12 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/client.pyi +2 -2
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/mount.py +124 -4
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/mount.pyi +16 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/runner.py +2 -7
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/volume.py +7 -48
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/volume.pyi +0 -40
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/api.proto +4 -4
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/api_pb2.pyi +4 -4
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_version/__init__.py +1 -1
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/LICENSE +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/README.md +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/__main__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_clustered_functions.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_clustered_functions.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_container_entrypoint.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_functions.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_ipython.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_location.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_object.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_output.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_partial_function.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_pty.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_resolver.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_resources.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_runtime/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_runtime/asgi.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_runtime/execution_context.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_runtime/telemetry.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_serialization.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_traceback.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_tunnel.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_tunnel.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_type_manager.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/app_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/async_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/blob_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/deprecation.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/docker_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/function_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/git_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/hash_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/http_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/logger.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/mount_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/name_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/package_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/shell_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_utils/time_utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_vendor/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_vendor/tblib.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/_watcher.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/app.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/app.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/call_graph.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/_download.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/_traceback.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/app.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/cluster.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/config.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/container.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/dict.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/entry_point.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/environment.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/import_refs.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/launch.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/network_file_system.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/profile.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/programs/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/programs/vscode.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/queues.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/secret.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/token.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/utils.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cli/volume.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/client.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cls.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/cls.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/config.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/container_process.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/container_process.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/dict.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/dict.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/environments.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/environments.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/exception.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/experimental/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/experimental/ipython.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/file_io.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/file_io.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/file_pattern_matcher.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/functions.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/functions.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/gpu.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/image.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/image.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/io_streams.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/io_streams.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/network_file_system.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/network_file_system.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/object.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/object.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/output.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/parallel_map.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/parallel_map.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/partial_function.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/partial_function.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/proxy.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/proxy.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/py.typed +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/queue.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/queue.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/requirements/2023.12.312.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/requirements/2023.12.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/requirements/2024.04.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/requirements/2024.10.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/requirements/PREVIEW.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/requirements/README.md +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/requirements/base-images.json +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/retries.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/runner.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/running_app.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/sandbox.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/sandbox.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/schedule.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/scheduler_placement.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/secret.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/secret.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/serving.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/serving.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/snapshot.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/snapshot.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/stream_type.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/token_flow.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal/token_flow.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal.egg-info/requires.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal.egg-info/top_level.txt +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_docs/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/__init__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/api_grpc.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/api_pb2.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/modal_options_grpc.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/options.proto +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/options_grpc.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/options_pb2.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/options_pb2.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/options_pb2_grpc.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/options_pb2_grpc.pyi +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_proto/py.typed +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/modal_version/__main__.py +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/pyproject.toml +0 -0
- {modal-1.0.3.dev14 → modal-1.0.3.dev21}/setup.cfg +0 -0
@@ -171,6 +171,14 @@ def _write_local_result(result_path: str, res: Any):
|
|
171
171
|
fid.write(res)
|
172
172
|
|
173
173
|
|
174
|
+
def _validate_interactive_quiet_params(ctx):
|
175
|
+
interactive = ctx.obj["interactive"]
|
176
|
+
show_progress = ctx.obj["show_progress"]
|
177
|
+
|
178
|
+
if not show_progress and interactive:
|
179
|
+
raise InvalidError("To use interactive mode, remove the --quiet flag")
|
180
|
+
|
181
|
+
|
174
182
|
def _make_click_function(app, signature: CliRunnableSignature, inner: Callable[[tuple[str, ...], dict[str, Any]], Any]):
|
175
183
|
@click.pass_context
|
176
184
|
def f(ctx, **kwargs):
|
@@ -180,6 +188,8 @@ def _make_click_function(app, signature: CliRunnableSignature, inner: Callable[[
|
|
180
188
|
else:
|
181
189
|
args = ()
|
182
190
|
|
191
|
+
_validate_interactive_quiet_params(ctx)
|
192
|
+
|
183
193
|
show_progress: bool = ctx.obj["show_progress"]
|
184
194
|
with enable_output(show_progress):
|
185
195
|
with run_app(
|
@@ -291,6 +301,8 @@ def _get_click_command_for_local_entrypoint(app: App, entrypoint: LocalEntrypoin
|
|
291
301
|
assert len(args) == 0 and len(kwargs) == 0
|
292
302
|
args = ctx.args
|
293
303
|
|
304
|
+
_validate_interactive_quiet_params(ctx)
|
305
|
+
|
294
306
|
show_progress: bool = ctx.obj["show_progress"]
|
295
307
|
with enable_output(show_progress):
|
296
308
|
with run_app(
|
@@ -31,7 +31,7 @@ class _Client:
|
|
31
31
|
server_url: str,
|
32
32
|
client_type: int,
|
33
33
|
credentials: typing.Optional[tuple[str, str]],
|
34
|
-
version: str = "1.0.3.
|
34
|
+
version: str = "1.0.3.dev21",
|
35
35
|
): ...
|
36
36
|
def is_closed(self) -> bool: ...
|
37
37
|
@property
|
@@ -94,7 +94,7 @@ class Client:
|
|
94
94
|
server_url: str,
|
95
95
|
client_type: int,
|
96
96
|
credentials: typing.Optional[tuple[str, str]],
|
97
|
-
version: str = "1.0.3.
|
97
|
+
version: str = "1.0.3.dev21",
|
98
98
|
): ...
|
99
99
|
def is_closed(self) -> bool: ...
|
100
100
|
@property
|
@@ -21,7 +21,7 @@ from modal_version import __version__
|
|
21
21
|
|
22
22
|
from ._object import _get_environment_name, _Object
|
23
23
|
from ._resolver import Resolver
|
24
|
-
from ._utils.async_utils import aclosing, async_map, synchronize_api
|
24
|
+
from ._utils.async_utils import TaskContext, aclosing, async_map, synchronize_api
|
25
25
|
from ._utils.blob_utils import FileUploadSpec, blob_upload_file, get_file_upload_spec_from_path
|
26
26
|
from ._utils.deprecation import deprecation_warning
|
27
27
|
from ._utils.grpc_utils import retry_transient_errors
|
@@ -115,7 +115,8 @@ class _MountFile(_MountEntry):
|
|
115
115
|
def get_files_to_upload(self):
|
116
116
|
local_file = self.local_file.resolve()
|
117
117
|
if not local_file.exists():
|
118
|
-
|
118
|
+
msg = f"local file {local_file} does not exist"
|
119
|
+
raise FileNotFoundError(msg)
|
119
120
|
|
120
121
|
rel_filename = self.remote_path
|
121
122
|
yield local_file, rel_filename
|
@@ -144,10 +145,12 @@ class _MountDir(_MountEntry):
|
|
144
145
|
local_dir = self.local_dir.expanduser().absolute()
|
145
146
|
|
146
147
|
if not local_dir.exists():
|
147
|
-
|
148
|
+
msg = f"local dir {local_dir} does not exist"
|
149
|
+
raise FileNotFoundError(msg)
|
148
150
|
|
149
151
|
if not local_dir.is_dir():
|
150
|
-
|
152
|
+
msg = f"local dir {local_dir} is not a directory"
|
153
|
+
raise NotADirectoryError(msg)
|
151
154
|
|
152
155
|
if self.recursive:
|
153
156
|
gen = (os.path.join(root, name) for root, dirs, files in os.walk(local_dir) for name in files)
|
@@ -810,3 +813,120 @@ def _is_modal_path(remote_path: PurePosixPath):
|
|
810
813
|
if is_modal_path:
|
811
814
|
return True
|
812
815
|
return False
|
816
|
+
|
817
|
+
|
818
|
+
REMOTE_PACKAGES_PATH = "/__modal/deps"
|
819
|
+
REMOTE_SITECUSTOMIZE_PATH = "/pkg/sitecustomize.py"
|
820
|
+
|
821
|
+
SITECUSTOMIZE_CONTENT = f"""
|
822
|
+
# This file is automatically generated by Modal.
|
823
|
+
# It ensures that Modal's python dependencies are available in the Python PATH,
|
824
|
+
# while prioritizing user-installed packages.
|
825
|
+
import sys; sys.path.append('{REMOTE_PACKAGES_PATH}')
|
826
|
+
""".strip()
|
827
|
+
|
828
|
+
|
829
|
+
async def _create_single_mount(
|
830
|
+
client: _Client,
|
831
|
+
builder_version: str,
|
832
|
+
python_version: str,
|
833
|
+
platform: str,
|
834
|
+
arch: str,
|
835
|
+
uv_python_platform: str = None,
|
836
|
+
check_if_exists: bool = True,
|
837
|
+
):
|
838
|
+
import subprocess
|
839
|
+
import tempfile
|
840
|
+
|
841
|
+
profile_environment = config.get("environment")
|
842
|
+
abi_tag = "cp" + python_version.replace(".", "")
|
843
|
+
mount_name = f"{builder_version}-{abi_tag}-{platform}-{arch}"
|
844
|
+
uv_python_platform = uv_python_platform or f"{arch}-{platform}"
|
845
|
+
|
846
|
+
if check_if_exists:
|
847
|
+
try:
|
848
|
+
await Mount.from_name(mount_name, namespace=api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL).hydrate.aio(client)
|
849
|
+
print(f"✅ Found existing mount {mount_name} in global namespace.")
|
850
|
+
return
|
851
|
+
except modal.exception.NotFoundError:
|
852
|
+
pass
|
853
|
+
|
854
|
+
with tempfile.TemporaryDirectory() as tmpd:
|
855
|
+
print(f"📦 Building {mount_name}.")
|
856
|
+
requirements = os.path.join(os.path.dirname(__file__), f"requirements/{builder_version}.txt")
|
857
|
+
subprocess.run(
|
858
|
+
[
|
859
|
+
"uv",
|
860
|
+
"pip",
|
861
|
+
"install",
|
862
|
+
"--strict",
|
863
|
+
"--no-deps",
|
864
|
+
"--no-cache",
|
865
|
+
"-r",
|
866
|
+
requirements,
|
867
|
+
"--compile-bytecode",
|
868
|
+
"--target",
|
869
|
+
tmpd,
|
870
|
+
"--python-platform",
|
871
|
+
uv_python_platform,
|
872
|
+
"--python-version",
|
873
|
+
python_version,
|
874
|
+
],
|
875
|
+
check=True,
|
876
|
+
capture_output=True,
|
877
|
+
)
|
878
|
+
|
879
|
+
print(f"🌐 Downloaded and unpacked packages to {tmpd}.")
|
880
|
+
|
881
|
+
python_mount = Mount._from_local_dir(tmpd, remote_path=REMOTE_PACKAGES_PATH)
|
882
|
+
|
883
|
+
with tempfile.NamedTemporaryFile() as sitecustomize:
|
884
|
+
sitecustomize.write(
|
885
|
+
SITECUSTOMIZE_CONTENT.encode("utf-8"),
|
886
|
+
)
|
887
|
+
sitecustomize.flush()
|
888
|
+
|
889
|
+
python_mount = python_mount.add_local_file(
|
890
|
+
sitecustomize.name,
|
891
|
+
remote_path=REMOTE_SITECUSTOMIZE_PATH,
|
892
|
+
)
|
893
|
+
|
894
|
+
await python_mount._deploy.aio(
|
895
|
+
mount_name,
|
896
|
+
api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
|
897
|
+
environment_name=profile_environment,
|
898
|
+
client=client,
|
899
|
+
)
|
900
|
+
print(f"✅ Deployed mount {mount_name} to global namespace.")
|
901
|
+
|
902
|
+
|
903
|
+
async def _create_client_dependency_mounts(client=None, check_if_exists=True):
|
904
|
+
coros = []
|
905
|
+
for python_version in PYTHON_STANDALONE_VERSIONS:
|
906
|
+
# glibc >= 2.17
|
907
|
+
coros.append(
|
908
|
+
_create_single_mount(
|
909
|
+
client,
|
910
|
+
"PREVIEW",
|
911
|
+
python_version,
|
912
|
+
"manylinux_2_17",
|
913
|
+
"x86_64",
|
914
|
+
check_if_exists=check_if_exists,
|
915
|
+
)
|
916
|
+
)
|
917
|
+
# musl >= 1.2
|
918
|
+
coros.append(
|
919
|
+
_create_single_mount(
|
920
|
+
client,
|
921
|
+
"PREVIEW",
|
922
|
+
python_version,
|
923
|
+
"musllinux_1_2",
|
924
|
+
"x86_64",
|
925
|
+
uv_python_platform="x86_64-unknown-linux-musl",
|
926
|
+
check_if_exists=check_if_exists,
|
927
|
+
)
|
928
|
+
)
|
929
|
+
await TaskContext.gather(*coros)
|
930
|
+
|
931
|
+
|
932
|
+
create_client_dependency_mounts = synchronize_api(_create_client_dependency_mounts)
|
@@ -308,6 +308,22 @@ def _create_client_mount(): ...
|
|
308
308
|
def create_client_mount(): ...
|
309
309
|
def _get_client_mount(): ...
|
310
310
|
def _is_modal_path(remote_path: pathlib.PurePosixPath): ...
|
311
|
+
async def _create_single_mount(
|
312
|
+
client: modal.client._Client,
|
313
|
+
builder_version: str,
|
314
|
+
python_version: str,
|
315
|
+
platform: str,
|
316
|
+
arch: str,
|
317
|
+
uv_python_platform: str = None,
|
318
|
+
check_if_exists: bool = True,
|
319
|
+
): ...
|
320
|
+
async def _create_client_dependency_mounts(client=None, check_if_exists=True): ...
|
321
|
+
|
322
|
+
class __create_client_dependency_mounts_spec(typing_extensions.Protocol):
|
323
|
+
def __call__(self, /, client=None, check_if_exists=True): ...
|
324
|
+
async def aio(self, /, client=None, check_if_exists=True): ...
|
325
|
+
|
326
|
+
create_client_dependency_mounts: __create_client_dependency_mounts_spec
|
311
327
|
|
312
328
|
ROOT_DIR: pathlib.PurePosixPath
|
313
329
|
|
@@ -9,7 +9,6 @@ import dataclasses
|
|
9
9
|
import os
|
10
10
|
import time
|
11
11
|
import typing
|
12
|
-
import warnings
|
13
12
|
from collections.abc import AsyncGenerator
|
14
13
|
from multiprocessing.synchronize import Event
|
15
14
|
from typing import TYPE_CHECKING, Any, Optional, TypeVar
|
@@ -296,12 +295,8 @@ async def _run_app(
|
|
296
295
|
|
297
296
|
output_mgr = _get_output_manager()
|
298
297
|
if interactive and output_mgr is None:
|
299
|
-
|
300
|
-
|
301
|
-
"Use 'with modal.enable_output():' to enable interactive mode and see logs.",
|
302
|
-
stacklevel=2,
|
303
|
-
)
|
304
|
-
interactive = False
|
298
|
+
msg = "Interactive mode requires output to be enabled. (Use the the `modal.enable_output()` context manager.)"
|
299
|
+
raise InvalidError(msg)
|
305
300
|
|
306
301
|
running_app: RunningApp = await _init_local_app_new(
|
307
302
|
client,
|
@@ -45,7 +45,6 @@ from ._utils.blob_utils import (
|
|
45
45
|
BLOCK_SIZE,
|
46
46
|
FileUploadSpec,
|
47
47
|
FileUploadSpec2,
|
48
|
-
blob_iter,
|
49
48
|
blob_upload_file,
|
50
49
|
get_file_upload_spec_from_fileobj,
|
51
50
|
get_file_upload_spec_from_path,
|
@@ -400,7 +399,7 @@ class _Volume(_Object, type_prefix="vo"):
|
|
400
399
|
return [entry async for entry in self.iterdir(path, recursive=recursive)]
|
401
400
|
|
402
401
|
@live_method_gen
|
403
|
-
def read_file(self, path: str) -> AsyncIterator[bytes]:
|
402
|
+
async def read_file(self, path: str) -> AsyncIterator[bytes]:
|
404
403
|
"""
|
405
404
|
Read a file from the modal.Volume.
|
406
405
|
|
@@ -414,23 +413,6 @@ class _Volume(_Object, type_prefix="vo"):
|
|
414
413
|
print(len(data)) # == 1024 * 1024
|
415
414
|
```
|
416
415
|
"""
|
417
|
-
return self._read_file1(path) if self._is_v1 else self._read_file2(path)
|
418
|
-
|
419
|
-
async def _read_file1(self, path: str) -> AsyncIterator[bytes]:
|
420
|
-
req = api_pb2.VolumeGetFileRequest(volume_id=self.object_id, path=path)
|
421
|
-
try:
|
422
|
-
response = await retry_transient_errors(self._client.stub.VolumeGetFile, req)
|
423
|
-
except GRPCError as exc:
|
424
|
-
raise FileNotFoundError(exc.message) if exc.status == Status.NOT_FOUND else exc
|
425
|
-
# TODO(Jonathon): use ranged requests.
|
426
|
-
if response.WhichOneof("data_oneof") == "data":
|
427
|
-
yield response.data
|
428
|
-
return
|
429
|
-
else:
|
430
|
-
async for data in blob_iter(response.data_blob_id, self._client.stub):
|
431
|
-
yield data
|
432
|
-
|
433
|
-
async def _read_file2(self, path: str) -> AsyncIterator[bytes]:
|
434
416
|
req = api_pb2.VolumeGetFile2Request(volume_id=self.object_id, path=path)
|
435
417
|
|
436
418
|
try:
|
@@ -461,36 +443,9 @@ class _Volume(_Object, type_prefix="vo"):
|
|
461
443
|
Read volume file into file-like IO object.
|
462
444
|
"""
|
463
445
|
if progress_cb is None:
|
464
|
-
|
465
446
|
def progress_cb(*_, **__):
|
466
447
|
pass
|
467
448
|
|
468
|
-
if self._is_v1:
|
469
|
-
return await self._read_file_into_fileobj1(path, fileobj, progress_cb)
|
470
|
-
else:
|
471
|
-
return await self._read_file_into_fileobj2(path, fileobj, progress_cb)
|
472
|
-
|
473
|
-
async def _read_file_into_fileobj1(
|
474
|
-
self, path: str, fileobj: typing.IO[bytes], progress_cb: Callable[..., Any]
|
475
|
-
) -> int:
|
476
|
-
num_bytes_written = 0
|
477
|
-
|
478
|
-
async for chunk in self._read_file1(path):
|
479
|
-
num_chunk_bytes_written = 0
|
480
|
-
|
481
|
-
while num_chunk_bytes_written < len(chunk):
|
482
|
-
# TODO(dflemstr): this is a small write, but nonetheless might block the event loop for some time:
|
483
|
-
n = fileobj.write(chunk)
|
484
|
-
num_chunk_bytes_written += n
|
485
|
-
progress_cb(advance=n)
|
486
|
-
|
487
|
-
num_bytes_written += len(chunk)
|
488
|
-
|
489
|
-
return num_bytes_written
|
490
|
-
|
491
|
-
async def _read_file_into_fileobj2(
|
492
|
-
self, path: str, fileobj: typing.IO[bytes], progress_cb: Callable[..., Any]
|
493
|
-
) -> int:
|
494
449
|
req = api_pb2.VolumeGetFile2Request(volume_id=self.object_id, path=path)
|
495
450
|
|
496
451
|
try:
|
@@ -567,8 +522,12 @@ class _Volume(_Object, type_prefix="vo"):
|
|
567
522
|
like `os.rename()` and then `commit()` the volume. The `copy_files()` method is useful when you don't have
|
568
523
|
the volume mounted as a filesystem, e.g. when running a script on your local computer.
|
569
524
|
"""
|
570
|
-
|
571
|
-
|
525
|
+
if self._is_v1:
|
526
|
+
request = api_pb2.VolumeCopyFilesRequest(volume_id=self.object_id, src_paths=src_paths, dst_path=dst_path)
|
527
|
+
await retry_transient_errors(self._client.stub.VolumeCopyFiles, request, base_delay=1)
|
528
|
+
else:
|
529
|
+
request = api_pb2.VolumeCopyFiles2Request(volume_id=self.object_id, src_paths=src_paths, dst_path=dst_path)
|
530
|
+
await retry_transient_errors(self._client.stub.VolumeCopyFiles2, request, base_delay=1)
|
572
531
|
|
573
532
|
@live_method
|
574
533
|
async def batch_upload(self, force: bool = False) -> "_AbstractVolumeUploadContextManager":
|
@@ -87,20 +87,12 @@ class _Volume(modal._object._Object):
|
|
87
87
|
def iterdir(self, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]: ...
|
88
88
|
async def listdir(self, path: str, *, recursive: bool = False) -> list[FileEntry]: ...
|
89
89
|
def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
90
|
-
def _read_file1(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
91
|
-
def _read_file2(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
92
90
|
async def read_file_into_fileobj(
|
93
91
|
self,
|
94
92
|
path: str,
|
95
93
|
fileobj: typing.IO[bytes],
|
96
94
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
97
95
|
) -> int: ...
|
98
|
-
async def _read_file_into_fileobj1(
|
99
|
-
self, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
100
|
-
) -> int: ...
|
101
|
-
async def _read_file_into_fileobj2(
|
102
|
-
self, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
103
|
-
) -> int: ...
|
104
96
|
async def remove_file(self, path: str, recursive: bool = False) -> None: ...
|
105
97
|
async def copy_files(self, src_paths: collections.abc.Sequence[str], dst_path: str) -> None: ...
|
106
98
|
async def batch_upload(self, force: bool = False) -> _AbstractVolumeUploadContextManager: ...
|
@@ -236,18 +228,6 @@ class Volume(modal.object.Object):
|
|
236
228
|
|
237
229
|
read_file: __read_file_spec[typing_extensions.Self]
|
238
230
|
|
239
|
-
class ___read_file1_spec(typing_extensions.Protocol[SUPERSELF]):
|
240
|
-
def __call__(self, /, path: str) -> typing.Iterator[bytes]: ...
|
241
|
-
def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
242
|
-
|
243
|
-
_read_file1: ___read_file1_spec[typing_extensions.Self]
|
244
|
-
|
245
|
-
class ___read_file2_spec(typing_extensions.Protocol[SUPERSELF]):
|
246
|
-
def __call__(self, /, path: str) -> typing.Iterator[bytes]: ...
|
247
|
-
def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
248
|
-
|
249
|
-
_read_file2: ___read_file2_spec[typing_extensions.Self]
|
250
|
-
|
251
231
|
class __read_file_into_fileobj_spec(typing_extensions.Protocol[SUPERSELF]):
|
252
232
|
def __call__(
|
253
233
|
self,
|
@@ -266,26 +246,6 @@ class Volume(modal.object.Object):
|
|
266
246
|
|
267
247
|
read_file_into_fileobj: __read_file_into_fileobj_spec[typing_extensions.Self]
|
268
248
|
|
269
|
-
class ___read_file_into_fileobj1_spec(typing_extensions.Protocol[SUPERSELF]):
|
270
|
-
def __call__(
|
271
|
-
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
272
|
-
) -> int: ...
|
273
|
-
async def aio(
|
274
|
-
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
275
|
-
) -> int: ...
|
276
|
-
|
277
|
-
_read_file_into_fileobj1: ___read_file_into_fileobj1_spec[typing_extensions.Self]
|
278
|
-
|
279
|
-
class ___read_file_into_fileobj2_spec(typing_extensions.Protocol[SUPERSELF]):
|
280
|
-
def __call__(
|
281
|
-
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
282
|
-
) -> int: ...
|
283
|
-
async def aio(
|
284
|
-
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
285
|
-
) -> int: ...
|
286
|
-
|
287
|
-
_read_file_into_fileobj2: ___read_file_into_fileobj2_spec[typing_extensions.Self]
|
288
|
-
|
289
249
|
class __remove_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
290
250
|
def __call__(self, /, path: str, recursive: bool = False) -> None: ...
|
291
251
|
async def aio(self, /, path: str, recursive: bool = False) -> None: ...
|
@@ -1957,11 +1957,11 @@ message ImageMetadata {
|
|
1957
1957
|
// package name -> version. Empty if missing
|
1958
1958
|
map<string, string> python_packages = 2;
|
1959
1959
|
|
1960
|
-
// The working directory of the image,
|
1960
|
+
// The working directory of the image, as an absolute file path.
|
1961
1961
|
//
|
1962
|
-
//
|
1963
|
-
//
|
1964
|
-
//
|
1962
|
+
// For most images, this is not set, which means to use the default workdir:
|
1963
|
+
// - On function runners, the default is `/root` (home directory).
|
1964
|
+
// - For image builds and sandbox environments, it is `/`.
|
1965
1965
|
optional string workdir = 3;
|
1966
1966
|
|
1967
1967
|
// The version of glibc in this image, if any.
|
@@ -6313,11 +6313,11 @@ class ImageMetadata(google.protobuf.message.Message):
|
|
6313
6313
|
package name -> version. Empty if missing
|
6314
6314
|
"""
|
6315
6315
|
workdir: builtins.str
|
6316
|
-
"""The working directory of the image,
|
6316
|
+
"""The working directory of the image, as an absolute file path.
|
6317
6317
|
|
6318
|
-
|
6319
|
-
|
6320
|
-
|
6318
|
+
For most images, this is not set, which means to use the default workdir:
|
6319
|
+
- On function runners, the default is `/root` (home directory).
|
6320
|
+
- For image builds and sandbox environments, it is `/`.
|
6321
6321
|
"""
|
6322
6322
|
libc_version_info: builtins.str
|
6323
6323
|
"""The version of glibc in this image, if any."""
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|