From 7130561134137cf5a4f8534dd91cb3363626b67b Mon Sep 17 00:00:00 2001 From: Elias Freider Date: Tue, 25 Feb 2025 15:42:19 +0100 Subject: [PATCH] Adds modal shell support for -m flag --- modal/cli/run.py | 5 ++++- test/cli_test.py | 23 ++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/modal/cli/run.py b/modal/cli/run.py index 4e0597eb0..ccd91b83c 100644 --- a/modal/cli/run.py +++ b/modal/cli/run.py @@ -526,6 +526,9 @@ def shell( ), ), pty: Optional[bool] = typer.Option(default=None, help="Run the command using a PTY."), + use_module_mode: bool = typer.Option( + False, "-m", help="Interpret argument as a Python module path instead of a file/script path" + ), ): """Run a command or interactive shell inside a Modal container. @@ -584,7 +587,7 @@ def shell( exec(container_id=container_or_function, command=shlex.split(cmd), pty=pty) return - import_ref = parse_import_ref(container_or_function) + import_ref = parse_import_ref(container_or_function, use_module_mode=use_module_mode) runnable, all_usable_commands = import_and_filter( import_ref, base_cmd="modal shell", accept_local_entrypoint=False, accept_webhook=True ) diff --git a/test/cli_test.py b/test/cli_test.py index 52b74d85c..706955dee 100644 --- a/test/cli_test.py +++ b/test/cli_test.py @@ -470,24 +470,27 @@ async def _write(): app_file = Path("app_run_tests") / "default_app.py" +app_file_as_module = "app_run_tests.default_app" webhook_app_file = Path("app_run_tests") / "webhook.py" cls_app_file = Path("app_run_tests") / "cls.py" @skip_windows("modal shell is not supported on Windows.") @pytest.mark.parametrize( - ["rel_file", "suffix"], + ["flags", "rel_file", "suffix"], [ - (app_file, "::foo"), # Function is explicitly specified - (webhook_app_file, "::foo"), # Function is explicitly specified - (webhook_app_file, ""), # Function must be inferred + ([], app_file, "::foo"), # Function is explicitly specified + (["-m"], app_file_as_module, "::foo"), # Function is explicitly specified - module mode + ([], webhook_app_file, "::foo"), # Function is explicitly specified + ([], webhook_app_file, ""), # Function must be inferred # TODO: fix modal shell auto-detection of a single class, even if it has multiple methods - # (cls_app_file, ""), # Class must be inferred - # (cls_app_file, "AParametrized"), # class name - (cls_app_file, "::AParametrized.some_method"), # method name + # ([], cls_app_file, ""), # Class must be inferred + # ([], cls_app_file, "AParametrized"), # class name + ([], cls_app_file, "::AParametrized.some_method"), # method name ], ) -def test_shell(servicer, set_env_client, supports_dir, mock_shell_pty, rel_file, suffix): +def test_shell(servicer, set_env_client, mock_shell_pty, suffix, monkeypatch, supports_dir, rel_file, flags): + monkeypatch.chdir(supports_dir) fake_stdin, captured_out = mock_shell_pty fake_stdin.clear() @@ -495,9 +498,7 @@ def test_shell(servicer, set_env_client, supports_dir, mock_shell_pty, rel_file, shell_prompt = servicer.shell_prompt - fn = (supports_dir / rel_file).as_posix() - - _run(["shell", fn + suffix]) + _run(["shell"] + flags + [str(rel_file) + suffix]) # first captured message is the empty message the mock server sends assert captured_out == [(1, shell_prompt), (1, b"Hello World\n")]