Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds pending deprecations for Mount.from_* methods #2683

Merged
merged 10 commits into from
Jan 10, 2025
6 changes: 3 additions & 3 deletions modal/_utils/function_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,11 @@ def get_entrypoint_mount(self) -> list[_Mount]:
# make sure the function's own entrypoint is included:
if self._type == FunctionInfoType.PACKAGE:
if config.get("automount"):
return [_Mount.from_local_python_packages(self.module_name)]
return [_Mount._from_local_python_packages(self.module_name)]
elif not self.is_serialized():
# mount only relevant file and __init__.py:s
return [
_Mount.from_local_dir(
_Mount._from_local_dir(
self._base_dir,
remote_path=self._remote_dir,
recursive=True,
Expand All @@ -341,7 +341,7 @@ def get_entrypoint_mount(self) -> list[_Mount]:
remote_path = ROOT_DIR / Path(self._file).name
if not _is_modal_path(remote_path):
return [
_Mount.from_local_file(
_Mount._from_local_file(
self._file,
remote_path=remote_path,
)
Expand Down
3 changes: 1 addition & 2 deletions modal/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,9 @@ def __init__(

```python notest
image = modal.Image.debian_slim().pip_install(...)
mount = modal.Mount.from_local_dir("./config")
secret = modal.Secret.from_name("my-secret")
volume = modal.Volume.from_name("my-data")
app = modal.App(image=image, mounts=[mount], secrets=[secret], volumes={"/mnt/data": volume})
app = modal.App(image=image, secrets=[secret], volumes={"/mnt/data": volume})
```
"""
if name is not None and not isinstance(name, str):
Expand Down
2 changes: 1 addition & 1 deletion modal/cli/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def jupyter(
timeout: int = 3600,
image: str = "ubuntu:22.04",
add_python: Optional[str] = "3.11",
mount: Optional[str] = None, # Create a `modal.Mount` from a local directory.
mount: Optional[str] = None, # Adds a local directory to the jupyter container
volume: Optional[str] = None, # Attach a persisted `modal.Volume` by name (creating if missing).
detach: bool = False, # Run the app in "detached" mode to persist after local client disconnects
):
Expand Down
15 changes: 5 additions & 10 deletions modal/cli/programs/run_jupyter.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,20 @@
import webbrowser
from typing import Any

from modal import App, Image, Mount, Queue, Secret, Volume, forward
from modal import App, Image, Queue, Secret, Volume, forward

# Passed by `modal launch` locally via CLI, plumbed to remote runner through secrets.
args: dict[str, Any] = json.loads(os.environ.get("MODAL_LAUNCH_ARGS", "{}"))


app = App()
app.image = Image.from_registry(args.get("image"), add_python=args.get("add_python")).pip_install("jupyterlab")

image = Image.from_registry(args.get("image"), add_python=args.get("add_python")).pip_install("jupyterlab")

mount = (
Mount.from_local_dir(
if args.get("mount"):
image = image.add_local_dir(
args.get("mount"),
remote_path="/root/lab/mount",
)
if args.get("mount")
else None
)
mounts = [mount] if mount else []

volume = (
Volume.from_name(
Expand All @@ -55,12 +50,12 @@ def wait_for_port(url: str, q: Queue):


@app.function(
image=image,
cpu=args.get("cpu"),
memory=args.get("memory"),
gpu=args.get("gpu"),
timeout=args.get("timeout"),
secrets=[Secret.from_dict({"MODAL_LAUNCH_ARGS": json.dumps(args)})],
mounts=mounts,
volumes=volumes,
concurrency_limit=1 if volume else None,
)
Expand Down
15 changes: 5 additions & 10 deletions modal/cli/programs/vscode.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import webbrowser
from typing import Any

from modal import App, Image, Mount, Queue, Secret, Volume, forward
from modal import App, Image, Queue, Secret, Volume, forward

# Passed by `modal launch` locally via CLI, plumbed to remote runner through secrets.
args: dict[str, Any] = json.loads(os.environ.get("MODAL_LAUNCH_ARGS", "{}"))
Expand All @@ -23,7 +23,7 @@


app = App()
app.image = (
image = (
Image.from_registry(args.get("image"), add_python="3.11")
.apt_install("curl", "dumb-init", "git", "git-lfs")
.run_commands(
Expand All @@ -44,16 +44,11 @@
.env({"ENTRYPOINTD": ""})
)


mount = (
Mount.from_local_dir(
if args.get("mount"):
image = image.add_local_dir(
args.get("mount"),
remote_path="/home/coder/mount",
)
if args.get("mount")
else None
)
mounts = [mount] if mount else []

volume = (
Volume.from_name(
Expand All @@ -80,12 +75,12 @@ def wait_for_port(data: tuple[str, str], q: Queue):


@app.function(
image=image,
cpu=args.get("cpu"),
memory=args.get("memory"),
gpu=args.get("gpu"),
timeout=args.get("timeout"),
secrets=[Secret.from_dict({"MODAL_LAUNCH_ARGS": json.dumps(args)})],
mounts=mounts,
volumes=volumes,
concurrency_limit=1 if volume else None,
)
Expand Down
6 changes: 3 additions & 3 deletions modal/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ def add_local_file(self, local_path: Union[str, Path], remote_path: str, *, copy
if remote_path.endswith("/"):
remote_path = remote_path + Path(local_path).name

mount = _Mount.from_local_file(local_path, remote_path)
mount = _Mount._from_local_file(local_path, remote_path)
return self._add_mount_layer_or_copy(mount, copy=copy)

def add_local_dir(
Expand Down Expand Up @@ -795,7 +795,7 @@ def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
return _Image._from_args(
base_images={"base": self},
dockerfile_function=build_dockerfile,
context_mount_function=lambda: _Mount.from_local_file(local_path, remote_path=f"/{basename}"),
context_mount_function=lambda: _Mount._from_local_file(local_path, remote_path=f"/{basename}"),
)

def add_local_python_source(
Expand Down Expand Up @@ -833,7 +833,7 @@ def add_local_python_source(
)
```
"""
mount = _Mount.from_local_python_packages(*modules, ignore=ignore)
mount = _Mount._from_local_python_packages(*modules, ignore=ignore)
return self._add_mount_layer_or_copy(mount, copy=copy)

def copy_local_dir(
Expand Down
50 changes: 48 additions & 2 deletions modal/mount.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from ._resolver import Resolver
from ._utils.async_utils import aclosing, async_map, synchronize_api
from ._utils.blob_utils import FileUploadSpec, blob_upload_file, get_file_upload_spec_from_path
from ._utils.deprecation import renamed_parameter
from ._utils.deprecation import deprecation_warning, renamed_parameter
from ._utils.grpc_utils import retry_transient_errors
from ._utils.name_utils import check_object_name
from ._utils.package_utils import get_module_mount_info
Expand All @@ -48,6 +48,11 @@
"3.13": ("20241008", "3.13.0"),
}

MOUNT_DEPRECATION_MESSAGE_PATTERN = """modal.Mount usage will soon be deprecated.

Use {replacement} instead, which is functionally and performance-wise equivalent.
"""


def client_mount_name() -> str:
"""Get the deployed name of the client package mount."""
Expand Down Expand Up @@ -401,6 +406,23 @@ def from_local_dir(
)
```
"""
deprecation_warning(
(2024, 1, 8), MOUNT_DEPRECATION_MESSAGE_PATTERN.format(replacement="image.add_local_dir"), pending=True
)
return _Mount._from_local_dir(local_path, remote_path=remote_path, condition=condition, recursive=recursive)

@staticmethod
def _from_local_dir(
local_path: Union[str, Path],
*,
# Where the directory is placed within in the mount
remote_path: Union[str, PurePosixPath, None] = None,
# Predicate filter function for file selection, which should accept a filepath and return `True` for inclusion.
# Defaults to including all files.
condition: Optional[Callable[[str], bool]] = None,
# add files from subdirectories as well
recursive: bool = True,
) -> "_Mount":
return _Mount._new().add_local_dir(
local_path, remote_path=remote_path, condition=condition, recursive=recursive
)
Expand Down Expand Up @@ -439,6 +461,13 @@ def from_local_file(local_path: Union[str, Path], remote_path: Union[str, PurePo
)
```
"""
deprecation_warning(
(2024, 1, 8), MOUNT_DEPRECATION_MESSAGE_PATTERN.format(replacement="image.add_local_file"), pending=True
)
return _Mount._from_local_file(local_path, remote_path)

@staticmethod
def _from_local_file(local_path: Union[str, Path], remote_path: Union[str, PurePosixPath, None] = None) -> "_Mount":
return _Mount._new().add_local_file(local_path, remote_path=remote_path)

@staticmethod
Expand Down Expand Up @@ -601,7 +630,24 @@ def f():
my_local_module.do_stuff()
```
"""
deprecation_warning(
(2024, 1, 8),
MOUNT_DEPRECATION_MESSAGE_PATTERN.format(replacement="image.add_local_python_source"),
pending=True,
)
return _Mount._from_local_python_packages(
*module_names, remote_dir=remote_dir, condition=condition, ignore=ignore
)

@staticmethod
def _from_local_python_packages(
*module_names: str,
remote_dir: Union[str, PurePosixPath] = ROOT_DIR.as_posix(),
# Predicate filter function for file selection, which should accept a filepath and return `True` for inclusion.
# Defaults to including all files.
condition: Optional[Callable[[str], bool]] = None,
ignore: Optional[Union[Sequence[str], Callable[[Path], bool]]] = None,
) -> "_Mount":
# Don't re-run inside container.

if condition is not None:
Expand Down Expand Up @@ -786,7 +832,7 @@ def get_auto_mounts() -> list[_Mount]:

try:
# at this point we don't know if the sys.modules module should be mounted or not
potential_mount = _Mount.from_local_python_packages(module_name)
potential_mount = _Mount._from_local_python_packages(module_name)
mount_paths = potential_mount._top_level_paths()
except ModuleNotMountable:
# this typically happens if the module is a built-in, has binary components or doesn't exist
Expand Down
2 changes: 1 addition & 1 deletion modal_global_objects/mounts/python_standalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def publish_python_standalone_mount(client, version: str) -> None:
urllib.request.urlretrieve(url, f"{d}/cpython.tar.gz")
shutil.unpack_archive(f"{d}/cpython.tar.gz", d)
print(f"🌐 Downloaded and unpacked archive to {d}.")
python_mount = Mount.from_local_dir(f"{d}/python")
python_mount = Mount._from_local_dir(f"{d}/python")
python_mount._deploy(
mount_name,
api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
Expand Down
Loading