Skip to content

Commit

Permalink
Rudimentary version for symlinks
Browse files Browse the repository at this point in the history
  • Loading branch information
abhaasgoyal committed Mar 1, 2024
1 parent 3783439 commit 3f2f731
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 9 deletions.
7 changes: 6 additions & 1 deletion benchcab/benchcab.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from benchcab.utils.pbs import render_job_script
from benchcab.utils.repo import create_repo
from benchcab.utils.subprocess import SubprocessWrapper, SubprocessWrapperInterface
from benchcab.workdir import setup_fluxsite_directory_tree
from benchcab.workdir import clean_directory_tree, clean_cwd_logfiles, setup_fluxsite_directory_tree


class Benchcab:
Expand Down Expand Up @@ -308,6 +308,11 @@ def fluxsite(self, config_path: str, no_submit: bool, skip: list[str]):
else:
self.fluxsite_submit_job(config_path, skip)

def clean(self, config_path: str):
"""Endpoint for cleaning runs uirectory ig."""
clean_directory_tree()
clean_cwd_logfiles()

def spatial(self, config_path: str):
"""Endpoint for `benchcab spatial`."""

Expand Down
11 changes: 11 additions & 0 deletions benchcab/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,15 @@ def generate_parser(app: Benchcab) -> argparse.ArgumentParser:
)
parser_spatial.set_defaults(func=app.spatial)

# subcommand: 'benchcab clean'
parser_spatial = subparsers.add_parser(
"clean",
parents=[args_help, args_subcommand],
help="Cleanup files created by running benchcab.",
description="""Removes src/ and runs/ directories, along with log files in the
project root directory.""",
add_help=False,
)
parser_spatial.set_defaults(func=app.clean)

return main_parser
11 changes: 9 additions & 2 deletions benchcab/data/config-schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ realisations:
schema:
git:
type: "dict"
excludes: "svn"
excludes: ["svn", "local"]
schema:
branch:
type: "string"
Expand All @@ -37,13 +37,20 @@ realisations:
required: false
svn:
type: "dict"
excludes: "git"
excludes: ["git", "local"]
schema:
branch_path:
type: "string"
required: true
revision:
type: "integer"
local:
type: "dict"
excludes: ["git", "svn"]
schema:
local_path:
type: "string"
required: true
name:
nullable: true
type: "string"
Expand Down
4 changes: 2 additions & 2 deletions benchcab/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from benchcab.environment_modules import EnvironmentModules, EnvironmentModulesInterface
from benchcab.utils import get_logger
from benchcab.utils.fs import chdir, copy2, rename
from benchcab.utils.repo import GitRepo, Repo
from benchcab.utils.repo import GitRepo, LocalRepo, Repo
from benchcab.utils.subprocess import SubprocessWrapper, SubprocessWrapperInterface


Expand Down Expand Up @@ -62,7 +62,7 @@ def __init__(
# TODO(Sean) we should not have to know whether `repo` is a `GitRepo` or
# `SVNRepo`, we should only be working with the `Repo` interface.
# See issue https://github.com/CABLE-LSM/benchcab/issues/210
if isinstance(repo, GitRepo):
if isinstance(repo, (GitRepo, LocalRepo)):
self.src_dir = Path("src")

@property
Expand Down
45 changes: 45 additions & 0 deletions benchcab/utils/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,49 @@ def get_branch_name(self) -> str:
"""


class LocalRepo(Repo):
"""Concrete implementation of the `Repo` class using local path backend."""

def __init__(self, local_path: str, path: str) -> None:
"""Return a LocalRepo instance.
Parameters
----------
path : str
Path of local CABLE branch
"""
self.name = Path(local_path).name
self.local_path = local_path
self.path = path / self.name if path.is_dir() else path
self.logger = get_logger()

def checkout(self):
"""Checkout the source code."""
self.path.symlink_to(self.local_path)
self.logger.info(f"Created symlink from to {self.path} named {self.name}")

def get_revision(self) -> str:
"""Return the latest revision of the source code.
Returns
-------
str
Human readable string describing the latest revision.
"""
return f"Using local CABLE branch: {self.name}"

def get_branch_name(self) -> str:
"""Return the branch name of the source code.
Returns
-------
str
Branch name of the source code.
"""
return Path(self.path).absolute()

class GitRepo(Repo):
"""A concrete implementation of the `Repo` class using a Git backend.
Expand Down Expand Up @@ -236,4 +279,6 @@ def create_repo(spec: dict, path: Path) -> Repo:
return GitRepo(path=path, **spec["git"])
if "svn" in spec:
return SVNRepo(svn_root=internal.CABLE_SVN_ROOT, path=path, **spec["svn"])
if "local" in spec:
return LocalRepo(path=path, **spec["local"])
raise RepoSpecError
12 changes: 10 additions & 2 deletions benchcab/workdir.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,23 @@
from benchcab import internal
from benchcab.utils.fs import mkdir


def clean_directory_tree():
"""Remove pre-existing directories in current working directory."""
"""Remove pre-existing directories and log files in current working directory."""
if internal.SRC_DIR.exists():
for realisation in internal.SRC_DIR.iterdir():
if realisation.is_symlink():
realisation.unlink()
shutil.rmtree(internal.SRC_DIR)

if internal.RUN_DIR.exists():
shutil.rmtree(internal.RUN_DIR)

def clean_cwd_logfiles():
for rev_log_file in internal.CWD.glob("rev_number-*.log"):
rev_log_file.unlink()

for pbs_job_file in internal.CWD.glob("benchcab_cable_qsub.sh*"):
pbs_job_file.unlink()

def setup_fluxsite_directory_tree():
"""Generate the directory structure used by `benchcab`."""
Expand Down
16 changes: 14 additions & 2 deletions tests/test_workdir.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
"""

from pathlib import Path
from benchcab import internal

import pytest

from benchcab.workdir import (
clean_directory_tree,
clean_cwd_logfiles,
setup_fluxsite_directory_tree,
)

Expand All @@ -37,12 +39,22 @@ def test_directory_structure_generated(self, fluxsite_directory_list):
assert path.exists()


class TestCleanDirectoryTree:
"""Tests for `clean_directory_tree()`."""
class TestCleanFiles:
"""Tests for `clean_directory_tree()` and `clean_cwd_logfiles)."""

@pytest.mark.parametrize("test_path", [Path("runs"), Path("src")])
def test_clean_directory_tree(self, test_path):
"""Success case: directory tree does not exist after clean."""
test_path.mkdir()
clean_directory_tree()
assert not test_path.exists()

@pytest.mark.parametrize("test_path", [Path("rev_number-200.log"), Path("benchcab_cable_qsub.sh.o21871")])
def test_clean_directory_tree(self, test_path: Path, monkeypatch):
"""Success case: log files in project root directory do not exist after clean."""
monkeypatch.setattr(internal, "CWD", Path.cwd())
test_path.touch()
clean_cwd_logfiles()

assert not test_path.exists()

0 comments on commit 3f2f731

Please sign in to comment.