Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Public driver rundir attr, redefine config_full #558

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion recipe/run_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ lint() {
msg Running linter
(
set -eux
pylint -j 1 .
pylint -j 4 .
)
msg OK
}
Expand Down
3 changes: 3 additions & 0 deletions src/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ pretty = true
warn_return_any = true

[tool.pylint.messages_control]
enable = [
"useless-suppression",
]
disable = [
"consider-using-f-string",
"duplicate-code",
Expand Down
2 changes: 1 addition & 1 deletion src/uwtools/api/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from types import ModuleType
from typing import Optional, Type, Union

from uwtools.drivers.driver import ( # pylint: disable=unused-import
from uwtools.drivers.driver import (
Assets,
AssetsCycleBased,
AssetsCycleLeadtimeBased,
Expand Down
2 changes: 0 additions & 2 deletions src/uwtools/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,8 +595,6 @@ def _dispatch_template_translate(args: Args) -> bool:

# Arguments

# pylint: disable=missing-function-docstring


def _add_arg_batch(group: Group) -> None:
group.add_argument(
Expand Down
8 changes: 4 additions & 4 deletions src/uwtools/drivers/cdeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def atm_nml(self):
"""
fn = "datm_in"
yield self._taskname(f"namelist file {fn}")
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
yield None
self._model_namelist_file("atm_in", path)
Expand All @@ -51,7 +51,7 @@ def atm_stream(self):
"""
fn = "datm.streams"
yield self._taskname(f"stream file {fn}")
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
template_file = self.config["atm_streams"]["template_file"]
yield file(path=Path(template_file))
Expand All @@ -75,7 +75,7 @@ def ocn_nml(self):
"""
fn = "docn_in"
yield self._taskname(f"namelist file {fn}")
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
yield None
self._model_namelist_file("ocn_in", path)
Expand All @@ -87,7 +87,7 @@ def ocn_stream(self):
"""
fn = "docn.streams"
yield self._taskname(f"stream file {fn}")
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
template_file = self.config["ocn_streams"]["template_file"]
yield file(path=Path(template_file))
Expand Down
2 changes: 1 addition & 1 deletion src/uwtools/drivers/chgres_cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def namelist_file(self):
"""
fn = "fort.41"
yield self._taskname(f"namelist file {fn}")
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
input_files = []
namelist = self.config[STR.namelist]
Expand Down
56 changes: 26 additions & 30 deletions src/uwtools/drivers/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,25 +45,23 @@ def __init__(
schema_file: Optional[Path] = None,
controller: Optional[str] = None,
) -> None:
config = config if isinstance(config, YAMLConfig) else YAMLConfig(config=config)
config.dereference(
config_input = config if isinstance(config, YAMLConfig) else YAMLConfig(config=config)
config_input.dereference(
context={
**({STR.cycle: cycle} if cycle else {}),
**({STR.leadtime: leadtime} if leadtime is not None else {}),
**config.data,
**config_input.data,
}
)
# The _config_full attribute points to the config block that includes the driver-specific
# block and any surrounding context, including e.g. platform: for some drivers.
self._config_full, _ = walk_key_path(config.data, key_path or [])
# The _config attribute points to the driver-specific config block. It is supplemented with
# config data from the controller block, if specified.
self._config_full: dict = config_input.data
config_intermediate, _ = walk_key_path(self._config_full, key_path or [])
self._platform = config_intermediate.get("platform")
try:
self._config = self._config_full[self._driver_name]
self._config: dict = config_intermediate[self._driver_name]
except KeyError as e:
raise UWConfigError("Required '%s' block missing in config" % self._driver_name) from e
if controller:
self._config[STR.rundir] = self._config_full[controller][STR.rundir]
self._config[STR.rundir] = config_intermediate[controller][STR.rundir]
self._validate(schema_file)
dryrun(enable=dry_run)

Expand All @@ -82,17 +80,24 @@ def __str__(self) -> str:
@property
def config(self) -> dict:
"""
A copy of the driver-specific config block.
A copy of the driver-specific config.
"""
return deepcopy(self._config)

@property
def config_full(self) -> dict:
"""
A copy of the driver-specific config block's parent block.
A copy of the original input config, dereferenced.
"""
return deepcopy(self._config_full)

@property
def rundir(self) -> Path:
"""
The path to the component's run directory.
"""
return Path(self.config[STR.rundir])

# Workflow tasks

@external
Expand Down Expand Up @@ -168,13 +173,6 @@ def _namelist_schema(
schema = schema[schema_key]
return schema

@property
def _rundir(self) -> Path:
"""
The path to the component's run directory.
"""
return Path(self.config[STR.rundir])

def _taskname(self, suffix: str) -> str:
"""
Returns a common tag for graph-task log messages.
Expand Down Expand Up @@ -328,7 +326,7 @@ def __init__(
)
self._batch = batch
if controller:
self._config[STR.execution] = self._config_full[controller][STR.execution]
self._config[STR.execution] = self.config_full[controller][STR.execution]

# Workflow tasks

Expand Down Expand Up @@ -375,11 +373,11 @@ def _run_via_local_execution(self):
A run executed directly on the local system.
"""
yield self._taskname("run via local execution")
path = self._rundir / self._runscript_done_file
path = self.rundir / self._runscript_done_file
yield asset(path, path.is_file)
yield self.provisioned_rundir()
cmd = "{x} >{x}.out 2>&1".format(x=self._runscript_path)
execute(cmd=cmd, cwd=self._rundir, log_output=True)
execute(cmd=cmd, cwd=self.rundir, log_output=True)

# Private helper methods

Expand All @@ -388,15 +386,13 @@ def _run_resources(self) -> dict[str, Any]:
"""
Returns platform configuration data.
"""
try:
platform = self.config_full[STR.platform]
except KeyError as e:
raise UWConfigError("Required 'platform' block missing in config") from e
if not self._platform:
raise UWConfigError("Required 'platform' block missing in config")
threads = self.config.get(STR.execution, {}).get(STR.threads)
return {
STR.account: platform[STR.account],
STR.rundir: self._rundir,
STR.scheduler: platform[STR.scheduler],
STR.account: self._platform[STR.account],
STR.rundir: self.rundir,
STR.scheduler: self._platform[STR.scheduler],
STR.stdout: "%s.out" % self._runscript_path.name, # config may override
**({STR.threads: threads} if threads else {}),
**self.config.get(STR.execution, {}).get(STR.batchargs, {}),
Expand Down Expand Up @@ -464,7 +460,7 @@ def _runscript_path(self) -> Path:
"""
Returns the path to the runscript.
"""
return self._rundir / f"runscript.{self._driver_name}"
return self.rundir / f"runscript.{self._driver_name}"

@property
def _scheduler(self) -> JobScheduler:
Expand Down
2 changes: 1 addition & 1 deletion src/uwtools/drivers/esg_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def namelist_file(self):
"""
fn = "regional_grid.nml"
yield self._taskname(fn)
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
base_file = self.config[STR.namelist].get(STR.basefile)
yield file(Path(base_file)) if base_file else None
Expand Down
2 changes: 1 addition & 1 deletion src/uwtools/drivers/filter_topo.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def namelist_file(self):
The namelist file.
"""
fn = "input.nml"
path = self._rundir / fn
path = self.rundir / fn
yield self._taskname(f"namelist file {fn}")
yield asset(path, path.is_file)
yield None
Expand Down
16 changes: 8 additions & 8 deletions src/uwtools/drivers/fv3.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def boundary_files(self):
for boundary_hour in range(offset, endhour, interval):
target = Path(lbcs["path"].format(tile=n, forecast_hour=boundary_hour))
linkname = (
self._rundir / "INPUT" / f"gfs_bndy.tile{n}.{(boundary_hour - offset):03d}.nc"
self.rundir / "INPUT" / f"gfs_bndy.tile{n}.{(boundary_hour - offset):03d}.nc"
)
symlinks[target] = linkname
yield [symlink(target=t, linkname=l) for t, l in symlinks.items()]
Expand All @@ -50,7 +50,7 @@ def diag_table(self):
"""
fn = "diag_table"
yield self._taskname(fn)
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
yield None
if src := self.config.get(fn):
Expand All @@ -66,7 +66,7 @@ def field_table(self):
"""
fn = "field_table"
yield self._taskname(fn)
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
yield filecopy(src=Path(self.config["field_table"][STR.basefile]), dst=path)

Expand All @@ -77,7 +77,7 @@ def files_copied(self):
"""
yield self._taskname("files copied")
yield [
filecopy(src=Path(src), dst=self._rundir / dst)
filecopy(src=Path(src), dst=self.rundir / dst)
for dst, src in self.config.get("files_to_copy", {}).items()
]

Expand All @@ -88,7 +88,7 @@ def files_linked(self):
"""
yield self._taskname("files linked")
yield [
symlink(target=Path(target), linkname=self._rundir / linkname)
symlink(target=Path(target), linkname=self.rundir / linkname)
for linkname, target in self.config.get("files_to_link", {}).items()
]

Expand All @@ -99,7 +99,7 @@ def model_configure(self):
"""
fn = "model_configure"
yield self._taskname(fn)
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
base_file = self.config["model_configure"].get(STR.basefile)
yield file(Path(base_file)) if base_file else None
Expand All @@ -116,7 +116,7 @@ def namelist_file(self):
"""
fn = "input.nml"
yield self._taskname(fn)
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
base_file = self.config[STR.namelist].get(STR.basefile)
yield file(Path(base_file)) if base_file else None
Expand Down Expand Up @@ -153,7 +153,7 @@ def restart_directory(self):
The RESTART directory.
"""
yield self._taskname("RESTART directory")
path = self._rundir / "RESTART"
path = self.rundir / "RESTART"
yield asset(path, path.is_dir)
yield None
path.mkdir(parents=True)
Expand Down
2 changes: 1 addition & 1 deletion src/uwtools/drivers/ioda.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def _runcmd(self) -> str:
Returns the full command-line component invocation.
"""
executable = self.config[STR.execution][STR.executable]
jedi_config = str(self._rundir / self._config_fn)
jedi_config = str(self.rundir / self._config_fn)
return " ".join([executable, jedi_config])


Expand Down
2 changes: 1 addition & 1 deletion src/uwtools/drivers/jedi.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _runcmd(self) -> str:
Returns the full command-line component invocation.
"""
execution = self.config[STR.execution]
jedi_config = self._rundir / self._config_fn
jedi_config = self.rundir / self._config_fn
mpiargs = execution.get(STR.mpiargs, [])
components = [
execution.get(STR.mpicmd), # MPI run program
Expand Down
6 changes: 3 additions & 3 deletions src/uwtools/drivers/jedi_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def configuration_file(self):
"""
fn = self._config_fn
yield self._taskname(fn)
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
base_file = self.config["configuration_file"].get(STR.basefile)
yield file(Path(base_file)) if base_file else None
Expand All @@ -44,7 +44,7 @@ def files_copied(self):
"""
yield self._taskname("files copied")
yield [
filecopy(src=Path(src), dst=self._rundir / dst)
filecopy(src=Path(src), dst=self.rundir / dst)
for dst, src in self.config.get("files_to_copy", {}).items()
]

Expand All @@ -55,7 +55,7 @@ def files_linked(self):
"""
yield self._taskname("files linked")
yield [
symlink(target=Path(target), linkname=self._rundir / linkname)
symlink(target=Path(target), linkname=self.rundir / linkname)
for linkname, target in self.config.get("files_to_link", {}).items()
]

Expand Down
4 changes: 2 additions & 2 deletions src/uwtools/drivers/mpas.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def boundary_files(self):
for boundary_hour in range(0, endhour + 1, interval):
file_date = self._cycle + timedelta(hours=boundary_hour)
fn = f"lbc.{file_date.strftime('%Y-%m-%d_%H.%M.%S')}.nc"
linkname = self._rundir / fn
linkname = self.rundir / fn
symlinks[linkname] = Path(lbcs["path"], fn)
yield [symlink(target=t, linkname=l) for l, t in symlinks.items()]

Expand All @@ -43,7 +43,7 @@ def namelist_file(self):
"""
The namelist file.
"""
path = self._rundir / "namelist.atmosphere"
path = self.rundir / "namelist.atmosphere"
yield self._taskname(str(path))
yield asset(path, path.is_file)
base_file = self.config[STR.namelist].get(STR.basefile)
Expand Down
6 changes: 3 additions & 3 deletions src/uwtools/drivers/mpas_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def files_copied(self):
"""
yield self._taskname("files copied")
yield [
filecopy(src=Path(src), dst=self._rundir / dst)
filecopy(src=Path(src), dst=self.rundir / dst)
for dst, src in self.config.get("files_to_copy", {}).items()
]

Expand All @@ -45,7 +45,7 @@ def files_linked(self):
"""
yield self._taskname("files linked")
yield [
symlink(target=Path(target), linkname=self._rundir / linkname)
symlink(target=Path(target), linkname=self.rundir / linkname)
for linkname, target in self.config.get("files_to_link", {}).items()
]

Expand Down Expand Up @@ -78,7 +78,7 @@ def streams_file(self):
"""
fn = self._streams_fn
yield self._taskname(fn)
path = self._rundir / fn
path = self.rundir / fn
yield asset(path, path.is_file)
yield None
streams = Element("streams")
Expand Down
Loading
Loading