Skip to content

Commit 5c2050d

Browse files
* runmodels WorkingDirectory is now a named argument: --workdir (#21)
* add `--clean` option for `runmodels` * consequent runs can alter some models * check if model working directory is non empty and clean it beforehand * propose --clean option
1 parent 051b0ca commit 5c2050d

File tree

4 files changed

+52
-18
lines changed

4 files changed

+52
-18
lines changed

.github/workflows/nrn-modeldb-ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ jobs:
116116
python -m pip install $NEURON_V1
117117
fi
118118
nrn_ver=`python -c "from neuron import __version__ as nrn_ver; print(nrn_ver)"`
119-
runmodels --virtual $nrn_ver $MODELS_TO_RUN
119+
runmodels --virtual --workdir=$nrn_ver $MODELS_TO_RUN
120120
report2html ${nrn_ver}.json
121121
if [[ -d "${DROP_DIR_V1}" ]]; then
122122
python -m pip uninstall --yes neuron-nightly==${nrn_ver}
@@ -136,7 +136,7 @@ jobs:
136136
python -m pip install $NEURON_V2
137137
fi
138138
nrn_ver=`python -c "from neuron import __version__ as nrn_ver; print(nrn_ver)"`
139-
runmodels --virtual $nrn_ver $MODELS_TO_RUN
139+
runmodels --virtual --workdir=$nrn_ver $MODELS_TO_RUN
140140
report2html ${nrn_ver}.json
141141
if [[ -d "${DROP_DIR_V2}" ]]; then
142142
python -m pip uninstall --yes neuron-nightly==${nrn_ver}

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ The following commands are now available:
2525
runmodels -h
2626
```
2727
Note that models have to be downloaded beforehand with `getmodels`.
28-
You can specify `--virtual` so that NEURON GUI is run in headless mode. It requires a backend (n.r. `Xvfb`)
28+
You can specify `--virtual` so that NEURON GUI is run in headless mode. It requires a backend (n.r. `Xvfb`).
29+
Re-running in the same `--workdir` can mangle results. Please use `--clean` if you wish to re-run in the same `--workdir`.
2930

3031

3132
* `report2html` -> create an interactive HTML report for a given json report (obtained with `runmodels`)
@@ -103,7 +104,7 @@ The generated report following `runmodels` contains te following info:
103104

104105
For example, by running:
105106
```
106-
runmodels --gout test3682 3682
107+
runmodels --gout --workdir=test3682 3682
107108
```
108109
we will generate report `test3782.json` :
109110
```yaml

modeldb/commands.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33
from os.path import abspath
44
from pprint import pprint
55
from . import config
6-
from .modelrun import ModelRunManager
6+
from .modelrun import ModelRunManager, is_dir_non_empty
77
from .modeldb import ModelDB
88
import inspect
99
import shlex
1010
import subprocess
1111
from jinja2 import Environment, FileSystemLoader
1212
import os
13+
import sys
1314
from .config import *
1415
from pathlib import Path
1516
from .report import diff_reports
1617
import json
18+
import shutil
1719

1820

1921
def runmodels(args=None):
@@ -22,28 +24,39 @@ def runmodels(args=None):
2224
Run nrn-modeldb-ci for all or specified models
2325
2426
Usage:
25-
runmodels <WorkingDirectory> [options] [<model_id>...]
27+
runmodels --workdir=<PATH> [options] [<model_id>...]
2628
runmodels -h Print help
2729
2830
Arguments:
29-
WorkingDirectory=PATH Required: directory where to run the models and store the reports
31+
--workdir=<PATH> Required: directory where to run the models and store the reports
3032
model_id=<n> Optional: ModelDB accession number(s) to run; default is all available models
3133
3234
Options:
3335
--gout Include gout into the report. Note that gout data can be very big, so disabled by default.
3436
--virtual Run in headless mode. You need a back-end like Xvfb.
37+
--clean Auto-clean model working directory before running (useful for consecutive runs and failsafe)
3538
3639
Examples
37-
runmodels /path/to/workdir
38-
runmodels /path/to/workdir 23613 12344
40+
runmodels --workdir=/path/to/workdir # run all models
41+
runmodels --clean --workdir=/path/to/workdir 23613 12344 # run models 23613 & 12344
3942
"""
4043
options = docopt(runmodels.__doc__, args)
41-
working_dir = options.pop("<WorkingDirectory>")
44+
working_dir = options.pop("--workdir")
4245
model_ids = [int(model_id) for model_id in options.pop("<model_id>")]
4346
gout = options.pop("--gout", False)
4447
virtual = options.pop("--virtual", False)
48+
clean = options.pop("--clean", False)
4549

46-
mrm = ModelRunManager(working_dir, gout=gout)
50+
if os.path.abspath(working_dir) == ROOT_DIR:
51+
print("Cannot run models directly into nrn-modeldb-ci ROOT_DIR -> {}".format(ROOT_DIR))
52+
sys.exit(1)
53+
54+
if not clean and is_dir_non_empty(working_dir):
55+
print("WARNING: WorkingDirectory {} exists and is non empty.".format(working_dir))
56+
print("\tre-run with --clean if you wish to overwrite model runs!")
57+
sys.exit(1)
58+
59+
mrm = ModelRunManager(working_dir, gout=gout, clean=clean)
4760
model_list = model_ids if model_ids else None
4861

4962
if virtual:
@@ -102,7 +115,7 @@ def diffgout(args=None):
102115
cmd = 'nrngui -c "strdef gout1" -c "gout1=\\"{}\\"" -c "strdef gout2" -c "gout2=\\"{}\\"" modeldb/showgout.hoc'.format(
103116
gout_file1, gout_file2)
104117
commands = shlex.split(cmd)
105-
p = subprocess.Popen(commands)
118+
_ = subprocess.Popen(commands)
106119

107120

108121
def modeldb_config(args=None):
@@ -204,4 +217,4 @@ def diffreports2html(args=None):
204217
diff_dict=diff_dict,
205218
gout_dict=gout_dict),
206219
)
207-
print('Done.')
220+
print('Done.')

modeldb/modelrun.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,26 @@
1313
import traceback
1414
import json
1515
import time
16+
import shutil
1617

1718
ModelDB = modeldb.ModelDB()
1819

20+
21+
def is_dir_non_empty(directory):
22+
"""
23+
Returns True if the `directory` exists and is non-empty
24+
"""
25+
try:
26+
if any(os.scandir(directory)):
27+
return True
28+
except Exception: # noqa
29+
pass
30+
31+
return False
32+
33+
1934
class ModelRun(dict):
20-
def __init__(self, model, working_dir):
35+
def __init__(self, model, working_dir, clean=False):
2136
self._model = model
2237
self._working_dir = os.path.abspath(working_dir)
2338
self._logs = []
@@ -27,6 +42,7 @@ def __init__(self, model, working_dir):
2742
self._no_mosinit_hoc = False
2843
self._run_time = 0
2944
self._run_py = False
45+
self._clean = clean
3046

3147
self["run_info"] = {}
3248

@@ -161,10 +177,13 @@ def prepare_model(model):
161177
with zipfile.ZipFile(
162178
os.path.join(MODELS_ZIP_DIR, str(model.id) + ".zip"), "r"
163179
) as zip_ref:
164-
zip_ref.extractall(model.working_dir)
165-
model.run_info["model_dir"] = os.path.join(
180+
model_dir = os.path.join(
166181
model.working_dir, os.path.dirname(zip_ref.infolist()[0].filename)
167182
)
183+
if model._clean and is_dir_non_empty(model_dir):
184+
shutil.rmtree(model_dir)
185+
zip_ref.extractall(model.working_dir)
186+
model.run_info["model_dir"] = model_dir
168187

169188
# write driver.hoc
170189
build_driver_hoc(model)
@@ -249,7 +268,7 @@ def run_model(model):
249268

250269

251270
class ModelRunManager(object):
252-
def __init__(self, master_dir, gout=False):
271+
def __init__(self, master_dir, gout=False, clean=False):
253272
self.master_dir = master_dir
254273
self.logfile = str(master_dir) + ".log"
255274
self.dumpfile = str(master_dir) + ".json"
@@ -259,6 +278,7 @@ def __init__(self, master_dir, gout=False):
259278
)
260279
self.run_logs = {}
261280
self._gout = gout
281+
self._clean = clean
262282

263283
def _setup_logging(self):
264284
self.logger = logging.getLogger("dev")
@@ -380,7 +400,7 @@ def run_models(self, model_list=None):
380400

381401
# prepare ModelRun objects
382402
models_to_run = (
383-
ModelRun(mdl, self.master_dir) for mdl in models_selected
403+
ModelRun(mdl, self.master_dir, self._clean) for mdl in models_selected
384404
)
385405

386406
# number of models

0 commit comments

Comments
 (0)