Skip to content

Commit

Permalink
re-factored to put the rose-changes in a separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
wxtim committed Nov 11, 2020
1 parent 094e911 commit 152efa7
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 142 deletions.
1 change: 0 additions & 1 deletion .github/workflows/bash.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ jobs:
- name: Install Cylc dependencies in the container
run: |
docker exec bash python3.7 -m pip install six==1.12
docker exec -w /root/cylc-flow bash python3.7 -m pip install git+https://github.com/metomi/rose@master
docker exec -w /root/cylc-flow bash python3.7 -m pip install .[all]
- name: Set the container bash version
Expand Down
2 changes: 1 addition & 1 deletion cylc/flow/parsec/fileparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

from cylc.flow import LOG
from cylc.flow.parsec.exceptions import ParsecError, FileParseError
from cylc.flow.parsec.jinja2support import get_rose_vars
from cylc.flow.parsec.rose_utils import get_rose_vars
from cylc.flow.parsec.OrderedDict import OrderedDictWithDefaults
from cylc.flow.parsec.include import inline
from cylc.flow.parsec.util import itemstr
Expand Down
68 changes: 1 addition & 67 deletions cylc/flow/parsec/jinja2support.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@

import importlib
import os
import sys
import pkgutil
import re
import shlex
import sys
import traceback
from glob import glob
from pathlib import Path

from jinja2 import (
BaseLoader,
Expand Down Expand Up @@ -188,70 +186,6 @@ def jinja2environment(dir_=None):
return env


def get_rose_vars(dir_=None, opts=None):
"""Load Jinja2 Vars from rose-suite.conf in dir_
Args:
dir_(string or Pathlib.path object):
Search for a ``rose-suite.conf`` file in this location.
opts:
Some sort of options object or string - To be used to allow CLI
specification of optional configuaration.
Returns:
If no ``rose-suite.conf`` file found then None.
Else return a dictionary of evaluated key value pairs from
the ``rose-suite.conf[jinja2:suite.rc]`` section.
TODO:
- Once the CLI for the ``rose suite-run`` replacement command is
ready plumb in the the equivelent of
``rose suite-run --opt-conf-key=""``.
- Condsider allowing ``[jinja2:flow.conf]`` as an alias for
consistency with cylc.
"""
# Return None if dir_ does not exist
if dir_ is None:
return None

# Return None if rose-suite.conf do not exist.
if isinstance(dir_, str):
dir_ = Path(dir_)
top_level_file = dir_ / 'rose-suite.conf'
if not top_level_file.is_file():
return None

from metomi.rose.config_tree import ConfigTreeLoader

opt_conf_keys = []
# get optional config key set as environment variable:
opt_conf_keys_env = os.getenv("ROSE_SUITE_OPT_CONF_KEYS")
if opt_conf_keys_env:
opt_conf_keys += shlex.split(opt_conf_keys_env)
# ... or as command line options
if 'opt_conf_keys' in dir(opts) and opts.opt_conf_keys:
opt_conf_keys += opts.opt_conf_keys

# Optional definitions
redefinitions = []
if 'defines' in dir(opts) and opts.defines:
redefinitions = opts.defines

# Load the actual config tree
config_tree = ConfigTreeLoader().load(
str(dir_),
'rose-suite.conf',
opt_keys=opt_conf_keys,
defines=redefinitions
)

# Get the jinja2 section of the config
config = config_tree.node.value['jinja2:suite.rc']
# Walk through the jinja2 section getting key=value pairs.
config = dict([(item[0][1], item[1].value) for item in config.walk()])
return config


def get_error_location():
"""Extract template line number from end of traceback.
Expand Down
89 changes: 89 additions & 0 deletions cylc/flow/parsec/rose_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# THIS FILE IS PART OF THE CYLC SUITE ENGINE.
# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Cylc support for reading and interpreting ``rose-suite.conf`` workflow
configuration files.
"""

import os
import shlex

from pathlib import Path

# from cylc.flow import LOG


def get_rose_vars(dir_=None, opts=None):
"""Load Jinja2 Vars from rose-suite.conf in dir_
Args:
dir_(string or Pathlib.path object):
Search for a ``rose-suite.conf`` file in this location.
opts:
Some sort of options object or string - To be used to allow CLI
specification of optional configuaration.
Returns:
If no ``rose-suite.conf`` file found then None.
Else return a dictionary of evaluated key value pairs from
the ``rose-suite.conf[jinja2:suite.rc]`` section.
TODO:
- Once the CLI for the ``rose suite-run`` replacement command is
ready plumb in the the equivelent of
``rose suite-run --opt-conf-key=""``.
- Condsider allowing ``[jinja2:flow.conf]`` as an alias for
consistency with cylc.
"""
# Return None if dir_ does not exist
if dir_ is None:
return None

# Return None if rose-suite.conf do not exist.
if isinstance(dir_, str):
dir_ = Path(dir_)
top_level_file = dir_ / 'rose-suite.conf'
if not top_level_file.is_file():
return None

from metomi.rose.config_tree import ConfigTreeLoader

opt_conf_keys = []
# get optional config key set as environment variable:
opt_conf_keys_env = os.getenv("ROSE_SUITE_OPT_CONF_KEYS")
if opt_conf_keys_env:
opt_conf_keys += shlex.split(opt_conf_keys_env)
# ... or as command line options
if 'opt_conf_keys' in dir(opts) and opts.opt_conf_keys:
opt_conf_keys += opts.opt_conf_keys

# Optional definitions
redefinitions = []
if 'defines' in dir(opts) and opts.defines:
redefinitions = opts.defines

# Load the actual config tree
config_tree = ConfigTreeLoader().load(
str(dir_),
'rose-suite.conf',
opt_keys=opt_conf_keys,
defines=redefinitions
)

# Get the jinja2 section of the config
config = config_tree.node.value['jinja2:suite.rc']
# Walk through the jinja2 section getting key=value pairs.
config = dict([(item[0][1], item[1].value) for item in config.walk()])
return config
73 changes: 0 additions & 73 deletions tests/unit/parsec/test_jinja2support.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,76 +107,3 @@ def test_pymoduleloader_invalid_module(tmp_path):
module_loader = PyModuleLoader()
with pytest.raises(jinja2.TemplateNotFound):
module_loader.load(environment=env, name='no way jose')


def test_get_rosedirs(tmp_path):
"""Function returns dict of [jinja2:suite.rc] items
Also check that function does _not_ return [env] section items.
Creates:
.
`--tmp_path
|-- rose-suite.conf
`-- opt
|-- rose-suite-gravy.conf
`-- rose-suite-chips.conf
Scenarios tested:
- Read in a basic rose-suite.conf file. Ensure we don't return env,
just jinja2.
- Get optional config name from an environment variable.
- Get optional config name from command line option.
- Get optional config name from an explicit over-ride string.
"""
with open(tmp_path / 'rose-suite.conf', 'w+') as testfh:
# The [env] section is there to make sure I don't load it with
# the jinja2 method.
testfh.write(
"[env]\n"
"TIMS_ENV_VAR=Jelly\n"
"[jinja2:suite.rc]\n"
"TIMS_JINJA2_ENV=64\n"
"Another_Jinja2_var=IceCream\n"
)

opt_dir = tmp_path / 'opt'
opt_dir.mkdir()
with open(opt_dir / 'rose-suite-gravy.conf', 'w+') as testfh:
testfh.write(
"[jinja2:suite.rc]\n"
"TIMS_JINJA2_ENV=42\n"
"Another_Jinja2_var=Peas\n"
)

with open(opt_dir / 'rose-suite-chips.conf', 'w+') as testfh:
testfh.write(
"[jinja2:suite.rc]\n"
"TIMS_JINJA2_ENV=99\n"
"Another_Jinja2_var=Chips\n"
)

assert get_rose_vars(tmp_path) == {
'Another_Jinja2_var': 'IceCream',
'TIMS_JINJA2_ENV': '64'
}

os.environ['ROSE_SUITE_OPT_CONF_KEYS'] = "gravy"
assert get_rose_vars(tmp_path) == {
'Another_Jinja2_var': 'Peas',
'TIMS_JINJA2_ENV': '42'
}

from types import SimpleNamespace
options = SimpleNamespace()
options.opt_conf_keys = ["chips"]
assert get_rose_vars(tmp_path, options) == {
'Another_Jinja2_var': 'Chips',
'TIMS_JINJA2_ENV': '99'
}

options.defines = ["[jinja2:suite.rc]TIMS_JINJA2_ENV=Curry_Sauce"]
assert get_rose_vars(tmp_path, options) == {
'Another_Jinja2_var': 'Chips',
'TIMS_JINJA2_ENV': 'Curry_Sauce'
}
92 changes: 92 additions & 0 deletions tests/unit/parsec/test_rose_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# THIS FILE IS PART OF THE CYLC SUITE ENGINE.
# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os

from cylc.flow.parsec.rose_utils import get_rose_vars


def test_get_rosedirs(tmp_path):
"""Function returns dict of [jinja2:suite.rc] items
Also check that function does _not_ return [env] section items.
Creates:
.
`--tmp_path
|-- rose-suite.conf
`-- opt
|-- rose-suite-gravy.conf
`-- rose-suite-chips.conf
Scenarios tested:
- Read in a basic rose-suite.conf file. Ensure we don't return env,
just jinja2.
- Get optional config name from an environment variable.
- Get optional config name from command line option.
- Get optional config name from an explicit over-ride string.
"""
with open(tmp_path / 'rose-suite.conf', 'w+') as testfh:
# The [env] section is there to make sure I don't load it with
# the jinja2 method.
testfh.write(
"[env]\n"
"TIMS_ENV_VAR=Jelly\n"
"[jinja2:suite.rc]\n"
"TIMS_JINJA2_ENV=64\n"
"Another_Jinja2_var=IceCream\n"
)

opt_dir = tmp_path / 'opt'
opt_dir.mkdir()
with open(opt_dir / 'rose-suite-gravy.conf', 'w+') as testfh:
testfh.write(
"[jinja2:suite.rc]\n"
"TIMS_JINJA2_ENV=42\n"
"Another_Jinja2_var=Peas\n"
)

with open(opt_dir / 'rose-suite-chips.conf', 'w+') as testfh:
testfh.write(
"[jinja2:suite.rc]\n"
"TIMS_JINJA2_ENV=99\n"
"Another_Jinja2_var=Chips\n"
)

assert get_rose_vars(tmp_path) == {
'Another_Jinja2_var': 'IceCream',
'TIMS_JINJA2_ENV': '64'
}

os.environ['ROSE_SUITE_OPT_CONF_KEYS'] = "gravy"
assert get_rose_vars(tmp_path) == {
'Another_Jinja2_var': 'Peas',
'TIMS_JINJA2_ENV': '42'
}

from types import SimpleNamespace
options = SimpleNamespace()
options.opt_conf_keys = ["chips"]
assert get_rose_vars(tmp_path, options) == {
'Another_Jinja2_var': 'Chips',
'TIMS_JINJA2_ENV': '99'
}

options.defines = ["[jinja2:suite.rc]TIMS_JINJA2_ENV=Curry_Sauce"]
assert get_rose_vars(tmp_path, options) == {
'Another_Jinja2_var': 'Chips',
'TIMS_JINJA2_ENV': 'Curry_Sauce'
}

0 comments on commit 152efa7

Please sign in to comment.