diff --git a/recipe/run_test.sh b/recipe/run_test.sh index 8f4753643..defba8696 100755 --- a/recipe/run_test.sh +++ b/recipe/run_test.sh @@ -23,7 +23,7 @@ lint() { msg Running linter ( set -eux - pylint -j 1 . + pylint -j 4 . ) msg OK } diff --git a/src/pyproject.toml b/src/pyproject.toml index e3c26057f..94bc782bf 100644 --- a/src/pyproject.toml +++ b/src/pyproject.toml @@ -26,6 +26,9 @@ pretty = true warn_return_any = true [tool.pylint.messages_control] +enable = [ + "useless-suppression", +] disable = [ "consider-using-f-string", "duplicate-code", diff --git a/src/uwtools/api/driver.py b/src/uwtools/api/driver.py index 7e1713983..8f88c7eb5 100644 --- a/src/uwtools/api/driver.py +++ b/src/uwtools/api/driver.py @@ -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, diff --git a/src/uwtools/cli.py b/src/uwtools/cli.py index 2259bbbf2..13892829a 100644 --- a/src/uwtools/cli.py +++ b/src/uwtools/cli.py @@ -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( diff --git a/src/uwtools/drivers/cdeps.py b/src/uwtools/drivers/cdeps.py index b93778eeb..1a776d7de 100644 --- a/src/uwtools/drivers/cdeps.py +++ b/src/uwtools/drivers/cdeps.py @@ -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) @@ -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)) @@ -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) @@ -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)) diff --git a/src/uwtools/drivers/chgres_cube.py b/src/uwtools/drivers/chgres_cube.py index b4ef57c6b..839a25916 100644 --- a/src/uwtools/drivers/chgres_cube.py +++ b/src/uwtools/drivers/chgres_cube.py @@ -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] diff --git a/src/uwtools/drivers/driver.py b/src/uwtools/drivers/driver.py index dbaac93aa..6f712223d 100644 --- a/src/uwtools/drivers/driver.py +++ b/src/uwtools/drivers/driver.py @@ -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) @@ -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 @@ -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. @@ -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 @@ -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 @@ -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, {}), @@ -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: diff --git a/src/uwtools/drivers/esg_grid.py b/src/uwtools/drivers/esg_grid.py index ba446eaa0..e8f3f5fb0 100644 --- a/src/uwtools/drivers/esg_grid.py +++ b/src/uwtools/drivers/esg_grid.py @@ -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 diff --git a/src/uwtools/drivers/filter_topo.py b/src/uwtools/drivers/filter_topo.py index 4bb58d0ac..18accdeab 100644 --- a/src/uwtools/drivers/filter_topo.py +++ b/src/uwtools/drivers/filter_topo.py @@ -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 diff --git a/src/uwtools/drivers/fv3.py b/src/uwtools/drivers/fv3.py index dde218ddf..da7a7a768 100644 --- a/src/uwtools/drivers/fv3.py +++ b/src/uwtools/drivers/fv3.py @@ -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()] @@ -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): @@ -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) @@ -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() ] @@ -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() ] @@ -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 @@ -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 @@ -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) diff --git a/src/uwtools/drivers/ioda.py b/src/uwtools/drivers/ioda.py index f169c15df..c41ace9a7 100644 --- a/src/uwtools/drivers/ioda.py +++ b/src/uwtools/drivers/ioda.py @@ -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]) diff --git a/src/uwtools/drivers/jedi.py b/src/uwtools/drivers/jedi.py index caf1bb9e6..88e445dfb 100644 --- a/src/uwtools/drivers/jedi.py +++ b/src/uwtools/drivers/jedi.py @@ -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 diff --git a/src/uwtools/drivers/jedi_base.py b/src/uwtools/drivers/jedi_base.py index c4fdfa4f2..a4270bf93 100644 --- a/src/uwtools/drivers/jedi_base.py +++ b/src/uwtools/drivers/jedi_base.py @@ -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 @@ -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() ] @@ -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() ] diff --git a/src/uwtools/drivers/mpas.py b/src/uwtools/drivers/mpas.py index 506290093..2085801f4 100644 --- a/src/uwtools/drivers/mpas.py +++ b/src/uwtools/drivers/mpas.py @@ -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()] @@ -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) diff --git a/src/uwtools/drivers/mpas_base.py b/src/uwtools/drivers/mpas_base.py index 41676b3af..e778a1a8d 100644 --- a/src/uwtools/drivers/mpas_base.py +++ b/src/uwtools/drivers/mpas_base.py @@ -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() ] @@ -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() ] @@ -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") diff --git a/src/uwtools/drivers/mpas_init.py b/src/uwtools/drivers/mpas_init.py index eeb4b7d29..d18324bd3 100644 --- a/src/uwtools/drivers/mpas_init.py +++ b/src/uwtools/drivers/mpas_init.py @@ -36,7 +36,7 @@ def boundary_files(self): file_date = self._cycle + timedelta(hours=boundary_hour) fn = f"FILE:{file_date.strftime('%Y-%m-%d_%H')}" target = Path(boundary_filepath, fn) - linkname = self._rundir / fn + linkname = self.rundir / fn symlinks[target] = linkname yield [symlink(target=t, linkname=l) for t, l in symlinks.items()] @@ -47,7 +47,7 @@ def namelist_file(self): """ fn = "namelist.init_atmosphere" 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 diff --git a/src/uwtools/drivers/schism.py b/src/uwtools/drivers/schism.py index 6dab0523a..5311429bd 100644 --- a/src/uwtools/drivers/schism.py +++ b/src/uwtools/drivers/schism.py @@ -27,7 +27,7 @@ def namelist_file(self): """ fn = "param.nml" yield self._taskname(fn) - path = self._rundir / fn + path = self.rundir / fn yield asset(path, path.is_file) template_file = Path(self.config[STR.namelist]["template_file"]) yield file(path=template_file) diff --git a/src/uwtools/drivers/sfc_climo_gen.py b/src/uwtools/drivers/sfc_climo_gen.py index ab2ea24ea..7f9862e16 100644 --- a/src/uwtools/drivers/sfc_climo_gen.py +++ b/src/uwtools/drivers/sfc_climo_gen.py @@ -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) vals = self.config[STR.namelist][STR.updatevalues]["config"] input_paths = [Path(v) for k, v in vals.items() if k.startswith("input_")] diff --git a/src/uwtools/drivers/ungrib.py b/src/uwtools/drivers/ungrib.py index 938c34ac7..d2fd85a29 100644 --- a/src/uwtools/drivers/ungrib.py +++ b/src/uwtools/drivers/ungrib.py @@ -37,7 +37,7 @@ def gribfiles(self): infile = Path( gfs_files["path"].format(cycle_hour=cycle_hour, forecast_hour=boundary_hour) ) - link_name = self._rundir / f"GRIBFILE.{_ext(n)}" + link_name = self.rundir / f"GRIBFILE.{_ext(n)}" links.append((infile, link_name)) yield [self._gribfile(infile, link) for infile, link in links] @@ -66,7 +66,7 @@ def namelist_file(self): }, } } - path = self._rundir / "namelist.wps" + path = self.rundir / "namelist.wps" yield self._taskname(str(path)) yield asset(path, path.is_file) yield None @@ -94,7 +94,7 @@ def vtable(self): """ A symlink to the Vtable file. """ - path = self._rundir / "Vtable" + path = self.rundir / "Vtable" yield self._taskname(str(path)) yield asset(path, path.is_symlink) infile = Path(self.config["vtable"]) diff --git a/src/uwtools/drivers/upp.py b/src/uwtools/drivers/upp.py index 1df762d0b..dcf2ef676 100644 --- a/src/uwtools/drivers/upp.py +++ b/src/uwtools/drivers/upp.py @@ -27,7 +27,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() ] @@ -38,7 +38,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() ] @@ -86,7 +86,7 @@ def _namelist_path(self) -> Path: """ Path to the namelist file. """ - return self._rundir / "itag" + return self.rundir / "itag" @property def _runcmd(self) -> str: diff --git a/src/uwtools/drivers/ww3.py b/src/uwtools/drivers/ww3.py index c81768d2d..977b71526 100644 --- a/src/uwtools/drivers/ww3.py +++ b/src/uwtools/drivers/ww3.py @@ -27,7 +27,7 @@ def namelist_file(self): """ fn = "ww3_shel.nml" yield self._taskname(fn) - path = self._rundir / fn + path = self.rundir / fn yield asset(path, path.is_file) template_file = Path(self.config[STR.namelist]["template_file"]) yield file(template_file) @@ -54,7 +54,7 @@ def restart_directory(self): The restart directory. """ yield self._taskname("restart directory") - path = self._rundir / "restart_wave" + path = self.rundir / "restart_wave" yield asset(path, path.is_dir) yield None path.mkdir(parents=True) diff --git a/src/uwtools/tests/api/test_config.py b/src/uwtools/tests/api/test_config.py index abe750442..c2cd84e64 100644 --- a/src/uwtools/tests/api/test_config.py +++ b/src/uwtools/tests/api/test_config.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-function-docstring,protected-access +# pylint: disable=missing-function-docstring import os from pathlib import Path diff --git a/src/uwtools/tests/api/test_driver.py b/src/uwtools/tests/api/test_driver.py index 27927c9df..e1a8f2cc8 100644 --- a/src/uwtools/tests/api/test_driver.py +++ b/src/uwtools/tests/api/test_driver.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-class-docstring,missing-function-docstring,protected-access,redefined-outer-name +# pylint: disable=missing-function-docstring,protected-access,redefined-outer-name import datetime as dt import logging diff --git a/src/uwtools/tests/config/test_support.py b/src/uwtools/tests/config/test_support.py index bec854c44..ddbae129b 100644 --- a/src/uwtools/tests/config/test_support.py +++ b/src/uwtools/tests/config/test_support.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-function-docstring,protected-access +# pylint: disable=missing-function-docstring """ Tests for uwtools.config.jinja2 module. """ diff --git a/src/uwtools/tests/drivers/test_cdeps.py b/src/uwtools/tests/drivers/test_cdeps.py index c74efe9e8..52877ac14 100644 --- a/src/uwtools/tests/drivers/test_cdeps.py +++ b/src/uwtools/tests/drivers/test_cdeps.py @@ -46,7 +46,7 @@ def test_CDEPS_atm(driverobj): @mark.parametrize("group", ["atm", "ocn"]) def test_CDEPS_nml(caplog, driverobj, group): log.setLevel(logging.DEBUG) - dst = driverobj._rundir / f"d{group}_in" + dst = driverobj.rundir / f"d{group}_in" assert not dst.is_file() del driverobj._config[f"{group}_in"]["base_file"] task = getattr(driverobj, f"{group}_nml") @@ -66,7 +66,7 @@ def test_CDEPS_ocn(driverobj): @mark.parametrize("group", ["atm", "ocn"]) def test_CDEPS_streams(driverobj, group): - dst = driverobj._rundir / f"d{group}.streams" + dst = driverobj.rundir / f"d{group}.streams" assert not dst.is_file() template = """ {{ streams.stream01.dtlimit }} @@ -84,7 +84,7 @@ def test_CDEPS_streams(driverobj, group): {{ streams.stream01.yearFirst }} {{ streams.stream01.yearLast }} """ - template_file = driverobj._rundir.parent / "template.jinja2" + template_file = driverobj.rundir.parent / "template.jinja2" with open(template_file, "w", encoding="utf-8") as f: print(dedent(template).strip(), file=f) driverobj._config[f"{group}_streams"]["template_file"] = template_file diff --git a/src/uwtools/tests/drivers/test_chgres_cube.py b/src/uwtools/tests/drivers/test_chgres_cube.py index ba651a1ad..d1597b00b 100644 --- a/src/uwtools/tests/drivers/test_chgres_cube.py +++ b/src/uwtools/tests/drivers/test_chgres_cube.py @@ -105,7 +105,7 @@ def test_ChgresCube(method): def test_ChgresCube_namelist_file(caplog, driverobj): log.setLevel(logging.DEBUG) - dst = driverobj._rundir / "fort.41" + dst = driverobj.rundir / "fort.41" assert not dst.is_file() path = Path(refs(driverobj.namelist_file())) assert dst.is_file() diff --git a/src/uwtools/tests/drivers/test_driver.py b/src/uwtools/tests/drivers/test_driver.py index c4624727f..1334e2742 100644 --- a/src/uwtools/tests/drivers/test_driver.py +++ b/src/uwtools/tests/drivers/test_driver.py @@ -218,7 +218,8 @@ def test_Assets_key_path(config, tmp_path): assetsobj = ConcreteAssetsTimeInvariant( config=config_file, dry_run=False, key_path=["foo", "bar"] ) - assert config == assetsobj.config_full + assert assetsobj.config == config[assetsobj._driver_name] + assert assetsobj._platform == config["platform"] def test_Assets_leadtime(config): @@ -262,7 +263,7 @@ def test_Assets__create_user_updated_config_base_file( def test_Assets__rundir(assetsobj): - assert assetsobj._rundir == Path(assetsobj.config["rundir"]) + assert assetsobj.rundir == Path(assetsobj.config["rundir"]) def test_Assets__validate_internal(assetsobj): @@ -372,7 +373,7 @@ def test_Driver__run_via_local_execution(driverobj): driverobj._run_via_local_execution() execute.assert_called_once_with( cmd="{x} >{x}.out 2>&1".format(x=driverobj._runscript_path), - cwd=driverobj._rundir, + cwd=driverobj.rundir, log_output=True, ) prd.assert_called_once_with() @@ -441,7 +442,7 @@ def test_Driver__namelist_schema_default_disable(driverobj): def test_Driver__run_resources_fail(driverobj): - del driverobj._config_full["platform"] + driverobj._platform = None with raises(UWConfigError) as e: assert driverobj._run_resources assert str(e.value) == "Required 'platform' block missing in config" @@ -454,7 +455,7 @@ def test_Driver__run_resources_pass(driverobj): driverobj._config["execution"].update({"batchargs": {"threads": 4, "walltime": walltime}}) assert driverobj._run_resources == { "account": account, - "rundir": driverobj._rundir, + "rundir": driverobj.rundir, "scheduler": scheduler, "stdout": "runscript.concrete.out", "threads": 4, diff --git a/src/uwtools/tests/drivers/test_esg_grid.py b/src/uwtools/tests/drivers/test_esg_grid.py index c7a0e0e14..11302218a 100644 --- a/src/uwtools/tests/drivers/test_esg_grid.py +++ b/src/uwtools/tests/drivers/test_esg_grid.py @@ -86,7 +86,7 @@ def test_ESGGrid(method): def test_ESGGrid_namelist_file(caplog, driverobj): log.setLevel(logging.DEBUG) - dst = driverobj._rundir / "regional_grid.nml" + dst = driverobj.rundir / "regional_grid.nml" assert not dst.is_file() path = Path(refs(driverobj.namelist_file())) assert dst.is_file() diff --git a/src/uwtools/tests/drivers/test_fv3.py b/src/uwtools/tests/drivers/test_fv3.py index dbe5a6502..9a720a3e5 100644 --- a/src/uwtools/tests/drivers/test_fv3.py +++ b/src/uwtools/tests/drivers/test_fv3.py @@ -103,19 +103,19 @@ def test_FV3(method): def test_FV3_boundary_files(driverobj): ns = (0, 1) - links = [driverobj._rundir / "INPUT" / f"gfs_bndy.tile7.{n:03d}.nc" for n in ns] + links = [driverobj.rundir / "INPUT" / f"gfs_bndy.tile7.{n:03d}.nc" for n in ns] assert not any(link.is_file() for link in links) for n in ns: - (driverobj._rundir / f"f{n}").touch() + (driverobj.rundir / f"f{n}").touch() driverobj.boundary_files() assert all(link.is_symlink() for link in links) def test_FV3_diag_table(driverobj): - src = driverobj._rundir / "diag_table.in" + src = driverobj.rundir / "diag_table.in" src.touch() driverobj._config["diag_table"] = src - dst = driverobj._rundir / "diag_table" + dst = driverobj.rundir / "diag_table" assert not dst.is_file() driverobj.diag_table() assert dst.is_file() @@ -127,9 +127,9 @@ def test_FV3_diag_table_warn(caplog, driverobj): def test_FV3_field_table(driverobj): - src = driverobj._rundir / "field_table.in" + src = driverobj.rundir / "field_table.in" src.touch() - dst = driverobj._rundir / "field_table" + dst = driverobj.rundir / "field_table" assert not dst.is_file() driverobj._config["field_table"] = {"base_file": str(src)} driverobj.field_table() @@ -161,11 +161,11 @@ def test_FV3_files_copied_and_linked(config, cycle, key, task, test, tmp_path): @mark.parametrize("base_file_exists", [True, False]) def test_FV3_model_configure(base_file_exists, caplog, driverobj): log.setLevel(logging.DEBUG) - src = driverobj._rundir / "model_configure.in" + src = driverobj.rundir / "model_configure.in" if base_file_exists: with open(src, "w", encoding="utf-8") as f: yaml.dump({}, f) - dst = driverobj._rundir / "model_configure" + dst = driverobj.rundir / "model_configure" assert not dst.is_file() driverobj._config["model_configure"] = {"base_file": src} driverobj.model_configure() @@ -178,10 +178,10 @@ def test_FV3_model_configure(base_file_exists, caplog, driverobj): def test_FV3_namelist_file(caplog, driverobj): log.setLevel(logging.DEBUG) - src = driverobj._rundir / "input.nml.in" + src = driverobj.rundir / "input.nml.in" with open(src, "w", encoding="utf-8") as f: yaml.dump({}, f) - dst = driverobj._rundir / "input.nml" + dst = driverobj.rundir / "input.nml" assert not dst.is_file() driverobj._config["namelist_file"] = {"base_file": src} path = Path(refs(driverobj.namelist_file())) @@ -232,7 +232,7 @@ def test_FV3_provisioned_rundir(domain, driverobj): def test_FV3_restart_directory(driverobj): - path = driverobj._rundir / "RESTART" + path = driverobj.rundir / "RESTART" assert not path.is_dir() driverobj.restart_directory() assert path.is_dir() diff --git a/src/uwtools/tests/drivers/test_ioda.py b/src/uwtools/tests/drivers/test_ioda.py index 0b6765e61..3adfa441b 100644 --- a/src/uwtools/tests/drivers/test_ioda.py +++ b/src/uwtools/tests/drivers/test_ioda.py @@ -109,7 +109,7 @@ def test_IODA__driver_name(driverobj): def test_IODA__runcmd(driverobj): - config = str(driverobj._rundir / driverobj._config_fn) + config = str(driverobj.rundir / driverobj._config_fn) assert driverobj._runcmd == f"/path/to/bufr2ioda.x {config}" diff --git a/src/uwtools/tests/drivers/test_jedi.py b/src/uwtools/tests/drivers/test_jedi.py index 24e79e52f..add007894 100644 --- a/src/uwtools/tests/drivers/test_jedi.py +++ b/src/uwtools/tests/drivers/test_jedi.py @@ -198,7 +198,7 @@ def test_JEDI__driver_name(driverobj): def test_JEDI__runcmd(driverobj): executable = driverobj.config["execution"]["executable"] - config = driverobj._rundir / driverobj._config_fn + config = driverobj.rundir / driverobj._config_fn assert ( driverobj._runcmd == f"srun --export=ALL --ntasks $SLURM_CPUS_ON_NODE {executable} {config}" ) diff --git a/src/uwtools/tests/drivers/test_mpas.py b/src/uwtools/tests/drivers/test_mpas.py index 2a0b58f4a..12b2601be 100644 --- a/src/uwtools/tests/drivers/test_mpas.py +++ b/src/uwtools/tests/drivers/test_mpas.py @@ -143,7 +143,7 @@ def test_MPAS(method): def test_MPAS_boundary_files(driverobj, cycle): ns = (0, 1) links = [ - driverobj._rundir / f"lbc.{(cycle+dt.timedelta(hours=n)).strftime('%Y-%m-%d_%H.%M.%S')}.nc" + driverobj.rundir / f"lbc.{(cycle+dt.timedelta(hours=n)).strftime('%Y-%m-%d_%H.%M.%S')}.nc" for n in ns ] assert not any(link.is_file() for link in links) @@ -180,7 +180,7 @@ def test_MPAS_files_copied_and_linked(config, cycle, key, task, test, tmp_path): def test_MPAS_namelist_file(caplog, driverobj): log.setLevel(logging.DEBUG) - dst = driverobj._rundir / "namelist.atmosphere" + dst = driverobj.rundir / "namelist.atmosphere" assert not dst.is_file() path = Path(refs(driverobj.namelist_file())) assert dst.is_file() @@ -193,7 +193,7 @@ def test_MPAS_namelist_file_long_duration(caplog, config, cycle): log.setLevel(logging.DEBUG) config["mpas"]["length"] = 120 driverobj = MPAS(config=config, cycle=cycle) - dst = driverobj._rundir / "namelist.atmosphere" + dst = driverobj.rundir / "namelist.atmosphere" assert not dst.is_file() path = Path(refs(driverobj.namelist_file())) assert dst.is_file() diff --git a/src/uwtools/tests/drivers/test_mpas_init.py b/src/uwtools/tests/drivers/test_mpas_init.py index a1d07dcb3..09b9a67e2 100644 --- a/src/uwtools/tests/drivers/test_mpas_init.py +++ b/src/uwtools/tests/drivers/test_mpas_init.py @@ -125,7 +125,7 @@ def test_MPASInit(method): def test_MPASInit_boundary_files(cycle, driverobj): ns = (0, 1) links = [ - driverobj._rundir / f"FILE:{(cycle+dt.timedelta(hours=n)).strftime('%Y-%m-%d_%H')}" + driverobj.rundir / f"FILE:{(cycle+dt.timedelta(hours=n)).strftime('%Y-%m-%d_%H')}" for n in ns ] assert not any(link.is_file() for link in links) @@ -157,7 +157,7 @@ def test_MPASInit_files_copied_and_linked(config, cycle, key, task, test, tmp_pa def test_MPASInit_namelist_contents(cycle, driverobj): - dst = driverobj._rundir / "namelist.init_atmosphere" + dst = driverobj.rundir / "namelist.init_atmosphere" assert not dst.is_file() driverobj.namelist_file() assert dst.is_file() @@ -170,7 +170,7 @@ def test_MPASInit_namelist_contents(cycle, driverobj): def test_MPASInit_namelist_file(caplog, driverobj): log.setLevel(logging.DEBUG) - dst = driverobj._rundir / "namelist.init_atmosphere" + dst = driverobj.rundir / "namelist.init_atmosphere" assert not dst.is_file() path = Path(refs(driverobj.namelist_file())) assert dst.is_file() diff --git a/src/uwtools/tests/drivers/test_schism.py b/src/uwtools/tests/drivers/test_schism.py index 5dc5cd366..d7a61fc18 100644 --- a/src/uwtools/tests/drivers/test_schism.py +++ b/src/uwtools/tests/drivers/test_schism.py @@ -55,7 +55,7 @@ def test_SCHISM_namelist_file(driverobj): src = driverobj.config["namelist"]["template_file"] with open(src, "w", encoding="utf-8") as f: yaml.dump({}, f) - dst = driverobj._rundir / "param.nml" + dst = driverobj.rundir / "param.nml" assert not dst.is_file() driverobj.namelist_file() assert dst.is_file() diff --git a/src/uwtools/tests/drivers/test_sfc_climo_gen.py b/src/uwtools/tests/drivers/test_sfc_climo_gen.py index 292a772e0..4d09843f3 100644 --- a/src/uwtools/tests/drivers/test_sfc_climo_gen.py +++ b/src/uwtools/tests/drivers/test_sfc_climo_gen.py @@ -108,7 +108,7 @@ def test_SfcClimoGen(method): def test_SfcClimoGen_namelist_file(caplog, driverobj): log.setLevel(logging.DEBUG) - dst = driverobj._rundir / "fort.41" + dst = driverobj.rundir / "fort.41" assert not dst.is_file() with patch.object(sfc_climo_gen, "file", new=ready): path = Path(refs(driverobj.namelist_file())) diff --git a/src/uwtools/tests/drivers/test_ungrib.py b/src/uwtools/tests/drivers/test_ungrib.py index 7d3f9499a..125731a78 100644 --- a/src/uwtools/tests/drivers/test_ungrib.py +++ b/src/uwtools/tests/drivers/test_ungrib.py @@ -81,7 +81,7 @@ def test_Ungrib_gribfiles(driverobj, tmp_path): links = [] cycle_hr = 12 for n, forecast_hour in enumerate((6, 12, 18)): - links = [driverobj._rundir / f"GRIBFILE.{ungrib._ext(n)}"] + links = [driverobj.rundir / f"GRIBFILE.{ungrib._ext(n)}"] infile = tmp_path / "gfs.t{cycle_hr:02d}z.pgrb2.0p25.f{forecast_hour:03d}".format( cycle_hr=cycle_hr, forecast_hour=forecast_hour ) @@ -92,7 +92,7 @@ def test_Ungrib_gribfiles(driverobj, tmp_path): def test_Ungrib_namelist_file(driverobj): - dst = driverobj._rundir / "namelist.wps" + dst = driverobj.rundir / "namelist.wps" assert not dst.is_file() driverobj.namelist_file() assert dst.is_file() @@ -116,10 +116,10 @@ def test_Ungrib_provisioned_rundir(driverobj): def test_Ungrib_vtable(driverobj): - src = driverobj._rundir / "Vtable.GFS.in" + src = driverobj.rundir / "Vtable.GFS.in" src.touch() driverobj._config["vtable"] = src - dst = driverobj._rundir / "Vtable" + dst = driverobj.rundir / "Vtable" assert not dst.is_symlink() driverobj.vtable() assert dst.is_symlink() @@ -130,9 +130,9 @@ def test_Ungrib__driver_name(driverobj): def test_Ungrib__gribfile(driverobj): - src = driverobj._rundir / "GRIBFILE.AAA.in" + src = driverobj.rundir / "GRIBFILE.AAA.in" src.touch() - dst = driverobj._rundir / "GRIBFILE.AAA" + dst = driverobj.rundir / "GRIBFILE.AAA" assert not dst.is_symlink() driverobj._gribfile(src, dst) assert dst.is_symlink() diff --git a/src/uwtools/tests/drivers/test_upp.py b/src/uwtools/tests/drivers/test_upp.py index 9132141fd..6ba234e27 100644 --- a/src/uwtools/tests/drivers/test_upp.py +++ b/src/uwtools/tests/drivers/test_upp.py @@ -101,20 +101,20 @@ def test_UPP_files_copied(driverobj): for _, src in driverobj.config["files_to_copy"].items(): Path(src).touch() for dst, _ in driverobj.config["files_to_copy"].items(): - assert not (driverobj._rundir / dst).is_file() + assert not (driverobj.rundir / dst).is_file() driverobj.files_copied() for dst, _ in driverobj.config["files_to_copy"].items(): - assert (driverobj._rundir / dst).is_file() + assert (driverobj.rundir / dst).is_file() def test_UPP_files_linked(driverobj): for _, src in driverobj.config["files_to_link"].items(): Path(src).touch() for dst, _ in driverobj.config["files_to_link"].items(): - assert not (driverobj._rundir / dst).is_file() + assert not (driverobj.rundir / dst).is_file() driverobj.files_linked() for dst, _ in driverobj.config["files_to_link"].items(): - assert (driverobj._rundir / dst).is_symlink() + assert (driverobj.rundir / dst).is_symlink() def test_UPP_namelist_file(caplog, driverobj): @@ -122,7 +122,7 @@ def test_UPP_namelist_file(caplog, driverobj): datestr = "2024-05-05_12:00:00" with open(driverobj.config["namelist"]["base_file"], "w", encoding="utf-8") as f: print("&model_inputs datestr='%s' / &nampgb kpv=88 /" % datestr, file=f) - dst = driverobj._rundir / "itag" + dst = driverobj.rundir / "itag" assert not dst.is_file() path = Path(refs(driverobj.namelist_file())) assert dst.is_file() @@ -172,7 +172,7 @@ def test_UPP__driver_name(driverobj): def test_UPP__namelist_path(driverobj): - assert driverobj._namelist_path == driverobj._rundir / "itag" + assert driverobj._namelist_path == driverobj.rundir / "itag" def test_UPP__runcmd(driverobj): diff --git a/src/uwtools/tests/drivers/test_ww3.py b/src/uwtools/tests/drivers/test_ww3.py index 9e8dd15aa..c67186fbc 100644 --- a/src/uwtools/tests/drivers/test_ww3.py +++ b/src/uwtools/tests/drivers/test_ww3.py @@ -55,7 +55,7 @@ def test_WaveWatchIII_namelist_file(driverobj): src = driverobj.config["namelist"]["template_file"] with open(src, "w", encoding="utf-8") as f: yaml.dump({}, f) - dst = driverobj._rundir / "ww3_shel.nml" + dst = driverobj.rundir / "ww3_shel.nml" assert not dst.is_file() driverobj.namelist_file() assert dst.is_file() @@ -73,7 +73,7 @@ def test_WaveWatchIII_provisioned_rundir(driverobj): def test_WaveWatchIII_restart_directory(driverobj): - path = driverobj._rundir / "restart_wave" + path = driverobj.rundir / "restart_wave" assert not path.is_dir() driverobj.restart_directory() assert path.is_dir() diff --git a/src/uwtools/tests/support.py b/src/uwtools/tests/support.py index 81d6d42c3..6759d266d 100644 --- a/src/uwtools/tests/support.py +++ b/src/uwtools/tests/support.py @@ -1,5 +1,3 @@ -# pylint: disable=missing-function-docstring - import re from copy import deepcopy from importlib import resources diff --git a/src/uwtools/tests/utils/test_file.py b/src/uwtools/tests/utils/test_file.py index 032e15e98..63d9b322d 100644 --- a/src/uwtools/tests/utils/test_file.py +++ b/src/uwtools/tests/utils/test_file.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-function-docstring,protected-access,redefined-outer-name +# pylint: disable=missing-function-docstring,protected-access """ Tests for uwtools.utils.file module. """ diff --git a/src/uwtools/utils/api.py b/src/uwtools/utils/api.py index dfe4b8932..4df3d2ad7 100644 --- a/src/uwtools/utils/api.py +++ b/src/uwtools/utils/api.py @@ -45,7 +45,7 @@ def make_execute( :param with_leadtime: Does the driver's constructor take a 'leadtime' parameter? """ - def execute( # pylint: disable=unused-argument + def execute( task: str, config: Optional[Union[Path, str]] = None, batch: bool = False, @@ -67,7 +67,7 @@ def execute( # pylint: disable=unused-argument stdin_ok=stdin_ok, ) - def execute_cycle( # pylint: disable=unused-argument + def execute_cycle( task: str, cycle: dt.datetime, config: Optional[Union[Path, str]] = None, @@ -90,7 +90,7 @@ def execute_cycle( # pylint: disable=unused-argument stdin_ok=stdin_ok, ) - def execute_cycle_leadtime( # pylint: disable=unused-argument + def execute_cycle_leadtime( task: str, cycle: dt.datetime, leadtime: dt.timedelta,