Skip to content

Commit

Permalink
Add support for executing single workflow in CLI
Browse files Browse the repository at this point in the history
Add tests for entry point

Add decorator to requirements
  • Loading branch information
ManInFez committed Oct 18, 2019
1 parent 872e625 commit 4c3165c
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 2 deletions.
20 changes: 19 additions & 1 deletion ert_shared/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from .models.ensemble_smoother import EnsembleSmoother
from .models.multiple_data_assimilation import MultipleDataAssimilation
from .models.single_test_run import SingleTestRun

import logging
logging.basicConfig(level=logging.INFO, format='%(message)s')

def run_cli(args):

Expand All @@ -20,6 +21,10 @@ def run_cli(args):
ert = EnKFMain(res_config, strict=True, verbose=args.verbose)
notifier = ErtCliNotifier(ert, args.config)
ERT.adapt(notifier)

if args.mode == 'workflow':
_execute_workflow(args.name)
return

# Setup model
if args.mode == 'test_run':
Expand All @@ -30,12 +35,25 @@ def run_cli(args):
model, argument = _setup_ensemble_smoother(args)
elif args.mode == 'es_mda':
model, argument = _setup_multiple_data_assimilation(args)

else:
raise NotImplementedError(
"Run type not supported {}".format(args.mode))

model.runSimulations(argument)

def _execute_workflow(workflow_name):
workflow_list = ERT.ert.getWorkflowList()
try:
workflow = workflow_list[workflow_name]
except KeyError:
logging.error("Workflow {} is not in the list of available workflows".format(workflow_name))
return
context = workflow_list.getContext()
workflow.run(ert=ERT.ert, verbose=True, context=context)
all_successfull = all([v['completed'] for k, v in workflow.getJobsReport().items()])
if all_successfull:
logging.info("Workflow {} ran successfully!".format(workflow_name))

def _setup_single_test_run():
model = SingleTestRun()
Expand Down
15 changes: 15 additions & 0 deletions ert_shared/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,21 @@ def get_ert_parser(parser=None):
es_mda_parser.set_defaults(func=run_cli)
es_mda_parser.add_argument("config", type=valid_file, help=config_help)


workflow_description = "Executes the workflow given"
workflow_parser = subparsers.add_parser(
"workflow", help=workflow_description, description=workflow_description
)
workflow_parser.add_argument(
help="Name of workflow",
dest="name"
)
workflow_parser.add_argument(
"--verbose", action="store_true", help="Show verbose output", default=False
)
workflow_parser.set_defaults(func=run_cli)
workflow_parser.add_argument("config", type=valid_file, help=config_help)

return parser


Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def package_files(directory):
'matplotlib<3',
'scipy',
'pytest',
'decorator'
],
zip_safe=False,
tests_require=['pytest', 'mock'],
Expand Down
26 changes: 26 additions & 0 deletions tests/global/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from tests import ErtTest
from res.test import ErtTestContext
from res.enkf import EnKFMain, ResConfig
import os
import subprocess
from res.enkf import EnKFMain, ResConfig
Expand All @@ -14,6 +15,11 @@
MultipleDataAssimilation
from ert_shared.models.single_test_run import SingleTestRun

import shutil
from tests.utils import tmpdir, SOURCE_DIR




class EntryPointTest(ErtTest):

Expand Down Expand Up @@ -179,3 +185,23 @@ def test_analysis_module_no_hit(self):
active_name, modules, iterable=True)

self.assertIsNone(name)

@tmpdir(os.path.join(SOURCE_DIR,'test-data/local/poly_example'))
def test_executing_workflow(self):
print("test")
with open('test_wf', 'w') as wf_file:
wf_file.write('EXPORT_RUNPATH')

config_file = 'poly.ert'
with open(config_file, 'a') as file:
file.write("LOAD_WORKFLOW test_wf")

rc = ResConfig(user_config_file=config_file)
rc.convertToCReference(None)
ert = EnKFMain(rc)
notifier = ErtCliNotifier(ert, config_file)
ERT.adapt(notifier)
args = Namespace(name="test_wf")
cli._execute_workflow(args.name)
assert os.path.isfile(".ert_runpath_list")

13 changes: 12 additions & 1 deletion tests/global/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,18 @@ def test_argparse_exec_es_mda_default_weights(self):
parsed.config, "test-data/local/poly_example/poly.ert")
self.assertEquals(parsed.weights, "4, 2, 1")
self.assertEquals(parsed.func.__name__, "run_cli")
self.assertFalse(parsed.verbose)
self.assertFalse(parsed.verbose)

def test_argparse_exec_workflow(self):
parser = ArgumentParser(prog="test_main")
parsed = ert_parser(
parser, ['workflow', "--verbose", "workflow_name", 'test-data/local/poly_example/poly.ert'])
self.assertEquals(parsed.mode, "workflow")
self.assertEquals(parsed.name, "workflow_name")
self.assertEquals(
parsed.config, "test-data/local/poly_example/poly.ert")
self.assertEquals(parsed.func.__name__, "run_cli")
self.assertTrue(parsed.verbose)

if __name__ == '__main__':
unittest.main()
61 changes: 61 additions & 0 deletions tests/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import contextlib
import logging
import os
import tempfile
import shutil

import decorator
import time

"""
Swiped from
https://github.com/equinor/everest/blob/master/tests/utils/__init__.py
"""

SOURCE_DIR = os.path.realpath(os.path.join(__file__, '../../../'))

def tmpdir(path=None, teardown=True):
""" Decorator based on the `tmp` context """
def real_decorator(function):
def wrapper(function, *args, **kwargs):
with tmp(path, teardown=teardown):
return function(*args, **kwargs)
return decorator.decorator(wrapper, function)
return real_decorator


@contextlib.contextmanager
def tmp(path=None, teardown=True):
"""Create and go into tmp directory, returns the path.
This function creates a temporary directory and enters that directory. The
returned object is the path to the created directory.
If @path is not specified, we create an empty directory, otherwise, it must
be a path to an existing directory. In that case, the directory will be
copied into the temporary directory.
If @teardown is True (defaults to True), the directory is (attempted)
deleted after context, otherwise it is kept as is.
"""
cwd = os.getcwd()
fname = tempfile.NamedTemporaryFile().name

if path:
if not os.path.isdir(path):
logging.debug('tmp:raise no such path')
raise IOError('No such directory: %s' % path)
shutil.copytree(path, fname)
else:
# no path to copy, create empty dir
os.mkdir(fname)

os.chdir(fname)

yield fname # give control to caller scope

os.chdir(cwd)

if teardown:
try:
shutil.rmtree(fname)
except OSError as oserr:
logging.debug('tmp:rmtree failed %s (%s)' % (fname, oserr))
shutil.rmtree(fname, ignore_errors=True)

0 comments on commit 4c3165c

Please sign in to comment.