Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cluv/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import subprocess
import sys
import typing
from pathlib import Path
from typing import Callable

import rich
Expand Down Expand Up @@ -215,6 +216,14 @@ def add_init_args(subparsers: Subparsers) -> argparse.ArgumentParser:
help="Initialize the current project across clusters.",
formatter_class=rich_argparse.RichHelpFormatter,
)
init_parser.add_argument(
"path",
nargs="?",
default=None,
metavar="<path>",
type=Path,
help="Path to initialize the project in. Creates the directory if it doesn't exist. Defaults to the current directory.",
)
init_parser.set_defaults(func=init)
return init_parser

Expand Down
6 changes: 5 additions & 1 deletion cluv/cli/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
REPO_ROOT = Path(__file__).resolve().parents[2]


def init() -> None:
def init(path: Path | None = None) -> None:
"""Initialize a new project with cluv.

This does the following:
Expand All @@ -30,6 +30,10 @@ def init() -> None:
console.rule("[bold cyan]cluv init[/bold cyan]")
console.print()

if path is not None:
path.mkdir(parents=True, exist_ok=True)
os.chdir(path)

# Check if the current directory is under the home directory. If not, raise an error and exit.
check_home_dir()

Expand Down
81 changes: 81 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
check_home_dir,
check_job_script,
check_symlink_to_scratch,
init,
)
from cluv.config import load_cluv_config

Expand Down Expand Up @@ -211,3 +212,83 @@ def test_replace_results_dir_from_legacy_template(
assert 'results_path="outputs"' in generated_legacy_script_content
assert "Using $results_path" in generated_legacy_script_content
assert "results_dir" not in generated_legacy_script_content


class TestInitPath:
def test_creates_directory_if_not_exists(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""init(path) should create the directory if it doesn't exist and chdir into it."""
import os

new_dir = tmp_path / "new_project"
assert not new_dir.exists()

chdir_calls: list[Path] = []
monkeypatch.setattr(os, "chdir", lambda p: chdir_calls.append(Path(p)))

# Patch the rest of init so we only test the path/mkdir behaviour
monkeypatch.setattr(CLUV_INIT_MODULE, "check_home_dir", lambda: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "run_uv_init", lambda: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "check_git", lambda: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "find_pyproject", lambda: tmp_path / "pyproject.toml")
monkeypatch.setattr(CLUV_INIT_MODULE, "check_cluv_config", lambda p: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "load_cluv_config", lambda p: type("C", (), {"clusters_names": [], "results_path": None})())
monkeypatch.setattr(CLUV_INIT_MODULE, "check_ssh_hostnames", lambda c: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "check_job_script", lambda r, p: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "check_symlink_to_scratch", lambda r, p: None)

init(path=new_dir)

assert new_dir.exists()
assert new_dir.is_dir()
assert chdir_calls == [new_dir]

def test_chdir_into_existing_directory(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""init(path) should chdir into an existing directory without error."""
import os

existing_dir = tmp_path / "existing_project"
existing_dir.mkdir()

chdir_calls: list[Path] = []
monkeypatch.setattr(os, "chdir", lambda p: chdir_calls.append(Path(p)))

monkeypatch.setattr(CLUV_INIT_MODULE, "check_home_dir", lambda: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "run_uv_init", lambda: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "check_git", lambda: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "find_pyproject", lambda: tmp_path / "pyproject.toml")
monkeypatch.setattr(CLUV_INIT_MODULE, "check_cluv_config", lambda p: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "load_cluv_config", lambda p: type("C", (), {"clusters_names": [], "results_path": None})())
monkeypatch.setattr(CLUV_INIT_MODULE, "check_ssh_hostnames", lambda c: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "check_job_script", lambda r, p: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "check_symlink_to_scratch", lambda r, p: None)

init(path=existing_dir)

assert chdir_calls == [existing_dir]

def test_no_chdir_when_path_is_none(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""init() without a path should not chdir."""
import os

chdir_calls: list[Path] = []
monkeypatch.setattr(os, "chdir", lambda p: chdir_calls.append(Path(p)))

monkeypatch.setattr(CLUV_INIT_MODULE, "check_home_dir", lambda: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "run_uv_init", lambda: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "check_git", lambda: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "find_pyproject", lambda: tmp_path / "pyproject.toml")
monkeypatch.setattr(CLUV_INIT_MODULE, "check_cluv_config", lambda p: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "load_cluv_config", lambda p: type("C", (), {"clusters_names": [], "results_path": None})())
monkeypatch.setattr(CLUV_INIT_MODULE, "check_ssh_hostnames", lambda c: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "check_job_script", lambda r, p: None)
monkeypatch.setattr(CLUV_INIT_MODULE, "check_symlink_to_scratch", lambda r, p: None)

init(path=None)

assert chdir_calls == []
Comment thread
lebrice marked this conversation as resolved.
Outdated
Loading