Skip to content

Commit

Permalink
Merge pull request #208 from snek5000/snek-make
Browse files Browse the repository at this point in the history
snek-make command
  • Loading branch information
paugier committed Nov 19, 2022
2 parents c62b635 + a035fef commit 632afa8
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 14 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ coverage_html:
black:
black src tests docs

isort:
isort src tests docs

list-sessions:
@nox --version 2>/dev/null || pip install nox
@$(NOX) -l
Expand Down
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ show_missing = true
exclude_lines = [
"if __name__ == .__main__.:",
'if "sphinx" in sys.modules:',
"raise ValueError",
"raise FileNotFoundError",
"raise NotImplementedError",
"raise ValueError",
"except KeyError:",
"except ImportError:",
"except AttributeError:",
"except NotImplementedError:"
"except NotImplementedError:",
"except StopIteration:",
]

[tool.coverage.html]
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ console_scripts =
snek-generate-config = snek5000.config:ensure_config_file
snek-info = snek5000.util.console:print_versions
snek-ipy-load = snek5000.util.console:start_ipython_load_sim
snek-make = snek5000.make:snek_make

[options.extras_require]
docs =
Expand Down
2 changes: 1 addition & 1 deletion src/snek5000/clusters.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@ def nproc_available():
var = "os.cpu_count()"
nproc = os.cpu_count()

logger.info(f"Using {var} to detect maximum number of processors available")
logger.debug(f"Using {var} to detect maximum number of processors available")
return nproc
96 changes: 87 additions & 9 deletions src/snek5000/make.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
======================
"""
import argparse
import sys
from pathlib import Path
from typing import Iterable
from warnings import warn
Expand All @@ -27,15 +29,30 @@ class Make:
"""

def __init__(self, sim):
self.sim = sim
self.path_run = sim.output.path_run
try:
self.file = next(f for f in sim.output.get_paths() if f.name == "Snakefile")
except AttributeError:
raise AttributeError("Unable to get path of Snakefile via Output class")
except StopIteration:
raise FileNotFoundError(f"No Snakefile in {self.path_run}")
def __init__(self, sim=None, path_run=None):

if (sim is None and path_run is None) or (
sim is not None and path_run is not None
):
raise ValueError("Either sim of path_run has to be given.")

if path_run is None:
self.path_run = sim.output.path_run
try:
self.file = next(
f for f in sim.output.get_paths() if f.name == "Snakefile"
)
except AttributeError:
raise AttributeError("Unable to get path of Snakefile via Output class")
except StopIteration:
raise FileNotFoundError(f"No Snakefile in {self.path_run}")
else:
if not path_run.exists():
raise FileNotFoundError(f"{path_run} does not exist.")
self.path_run = Path(path_run)
self.file = self.path_run / "Snakefile"
if not self.file.exists():
raise FileNotFoundError(f"No Snakefile in {path_run}")

self.log_handler = []

Expand Down Expand Up @@ -97,6 +114,8 @@ def exec(
It is also possible to do the same directly from command line
by changing to the simulation directory and executing::
snek-make -h
snek-make compile
snakemake -j1 compile
snakemake -j1 --set-resources='run:nproc=4' run
Expand All @@ -109,6 +128,7 @@ def exec(
or from command line with::
snek-make -l
snakemake --list-target-rules
.. seealso:: Useful Snakemake `command line arguments`_
Expand Down Expand Up @@ -255,3 +275,61 @@ def build(self, config):
return self.exec(*self.targets, config=config, force_incomplete=True)
else:
return True


def snek_make():
"""Used for the command snek-make"""
parser = argparse.ArgumentParser(
prog="snek-make",
description="Execute Snakemake rules",
formatter_class=argparse.RawDescriptionHelpFormatter,
)

parser.add_argument(
"rule",
nargs="?",
default=None,
help="Snakemake rule to be executed.",
)
parser.add_argument(
"-l",
"--list-rules",
action="store_true",
help="List rules and exit.",
)
parser.add_argument(
"-d",
"--dry-run",
action="store_true",
help="Dry run snakemake rules without executing.",
)
parser.add_argument(
"-np",
"--nproc",
type=int,
default=None,
help="Number of MPI processes.",
)
parser.add_argument(
"--clean-after-fail",
action="store_true",
help="Keep incomplete output files of failed jobs.",
)

args = parser.parse_args()

make = Make(path_run=Path.cwd())

if args.rule is None:
print("You need to specify a rule to be executed")

if args.list_rules or args.rule is None:
make.list()
sys.exit(0)

make.exec(
args.rule,
dryrun=args.dry_run,
keep_incomplete=not args.clean_after_fail,
nproc=args.nproc,
)
39 changes: 37 additions & 2 deletions tests/test_make.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import os
import sys
from concurrent.futures import ProcessPoolExecutor as Pool
from concurrent.futures import as_completed
from contextlib import contextmanager
from pathlib import Path
from unittest.mock import patch

import pytest

from snek5000.make import _Nek5000Make, snek_make

def nek5000_build(config):
from snek5000.make import _Nek5000Make

def nek5000_build(config):
nm = _Nek5000Make()
return nm.build(config)

Expand All @@ -28,3 +34,32 @@ def test_nek5000_make(pytestconfig):
]
for future in as_completed(simultaneous_builds):
assert future.result()


def test_snek_make_help():
with patch.object(sys, "argv", ["snek-make", "-h"]):
with pytest.raises(SystemExit):
snek_make()


@contextmanager
def current_dir_changed_to(path):
cwd = Path.cwd()
os.chdir(path)
try:
yield
finally:
os.chdir(cwd)


def test_snek_make_no_rule(sim_data):
with patch.object(sys, "argv", ["snek-make"]):
with pytest.raises(SystemExit):
with current_dir_changed_to(sim_data):
snek_make()


def test_snek_make_clean(sim_data):
with patch.object(sys, "argv", ["snek-make", "clean"]):
with current_dir_changed_to(sim_data):
snek_make()

0 comments on commit 632afa8

Please sign in to comment.