Skip to content

Commit

Permalink
2.3.2 (#519)
Browse files Browse the repository at this point in the history
* Fixing leadtime logic when processing 0-hr forecast (#514)

Updating logic to allow for 0-hour processing.

---------

Co-authored-by: Paul Madden <paul.madden@noaa.gov>

* update release build num

* Fix chgres_cube requirements. (#516)

Some configurations of the chgres_cube do not require all these file definitions, so check for existence before including.

---------

Co-authored-by: Paul Madden <136389411+maddenp-noaa@users.noreply.github.com>

---------

Co-authored-by: Christina Holt <56881914+christinaholtNOAA@users.noreply.github.com>
Co-authored-by: Paul Madden <paul.madden@noaa.gov>
Co-authored-by: Paul Madden <136389411+maddenp-noaa@users.noreply.github.com>
  • Loading branch information
4 people authored Jun 27, 2024
1 parent 9f272a9 commit b08be17
Show file tree
Hide file tree
Showing 12 changed files with 50 additions and 49 deletions.
2 changes: 1 addition & 1 deletion docs/sections/user_guide/cli/drivers/upp/run-help.out
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Required arguments:
--cycle CYCLE
The cycle in ISO8601 format (e.g. 2024-05-23T18)
--leadtime LEADTIME
The leadtime as HH[:MM[:SS]]
The leadtime as hours[:minutes[:seconds]]

Optional arguments:
-h, --help
Expand Down
2 changes: 1 addition & 1 deletion docs/sections/user_guide/cli/tools/file/copy-help.out
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Optional arguments:
--cycle CYCLE
The cycle in ISO8601 format (e.g. 2024-05-29T12)
--leadtime LEADTIME
The leadtime as HH[:MM[:SS]]
The leadtime as hours[:minutes[:seconds]]
--dry-run
Only log info, making no changes
--quiet, -q
Expand Down
2 changes: 1 addition & 1 deletion docs/sections/user_guide/cli/tools/file/link-help.out
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Optional arguments:
--cycle CYCLE
The cycle in ISO8601 format (e.g. 2024-05-29T12)
--leadtime LEADTIME
The leadtime as HH[:MM[:SS]]
The leadtime as hours[:minutes[:seconds]]
--dry-run
Only log info, making no changes
--quiet, -q
Expand Down
2 changes: 1 addition & 1 deletion recipe/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
"pyyaml =6.0.*"
]
},
"version": "2.3.1"
"version": "2.3.2"
}
15 changes: 6 additions & 9 deletions src/uwtools/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import datetime as dt
import json
import re
import sys
from argparse import ArgumentParser as Parser
from argparse import HelpFormatter
Expand All @@ -27,7 +28,7 @@
from uwtools.utils.file import get_file_format, resource_path

FORMATS = FORMAT.extensions()
LEADTIME_DESC = "HH[:MM[:SS]]"
LEADTIME_DESC = "hours[:minutes[:seconds]]"
TITLE_REQ_ARG = "Required arguments"

Args = Dict[str, Any]
Expand Down Expand Up @@ -995,7 +996,7 @@ def _dispatch_to_driver(name: str, args: Args) -> bool:
}
if cycle := args.get(STR.cycle):
kwargs[STR.cycle] = cycle
if leadtime := args.get(STR.leadtime):
if (leadtime := args.get(STR.leadtime)) is not None:
kwargs[STR.leadtime] = leadtime
return execute(**kwargs)

Expand Down Expand Up @@ -1077,13 +1078,9 @@ def _timedelta_from_str(tds: str) -> dt.timedelta:
:param tds: The timedelta string to parse.
"""
fmts = ("%H:%M:%S", "%H:%M", "%H")
for fmt in fmts:
try:
t = dt.datetime.strptime(tds, fmt)
return dt.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)
except ValueError:
pass
if matches := re.match(r"(\d+)(:(\d+))?(:(\d+))?", tds):
h, m, s = [int(matches.groups()[n] or 0) for n in (0, 2, 4)]
return dt.timedelta(hours=h, minutes=m, seconds=s)
_abort(f"Specify leadtime as {LEADTIME_DESC}")


Expand Down
1 change: 1 addition & 0 deletions src/uwtools/drivers/chgres_cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def namelist_file(self):
] + [
Path(config_files["data_dir_input_grid"]) / config_files[k]
for k in ("atm_files_input_grid", "grib2_file_input_grid", "sfc_files_input_grid")
if k in config_files
]
yield [file(input_path) for input_path in input_paths]
self._create_user_updated_config(
Expand Down
5 changes: 3 additions & 2 deletions src/uwtools/drivers/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,13 @@ def __init__(
dryrun(enable=dry_run)
self._config = YAMLConfig(config=config)
self._batch = batch
if leadtime and not cycle:
has_leadtime = leadtime is not None
if has_leadtime and not cycle:
raise UWError("When leadtime is specified, cycle is required")
self._config.dereference(
context={
**({"cycle": cycle} if cycle else {}),
**({"leadtime": leadtime} if leadtime else {}),
**({"leadtime": leadtime} if has_leadtime else {}),
**self._config.data,
}
)
Expand Down
2 changes: 1 addition & 1 deletion src/uwtools/resources/info.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "2.3.1",
"version": "2.3.2",
"buildnum": "0"
}
25 changes: 13 additions & 12 deletions src/uwtools/tests/drivers/test_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,22 +129,23 @@ def driverobj(config):
)


# Asset Tests
# Assets Tests


def test_Assets(assetobj):
assert Path(assetobj._driver_config["base_file"]).name == "base.yaml"
assert assetobj._batch is True


def test_Asset_cycle_leadtime_error(config):
@pytest.mark.parametrize("hours", [0, 24, 168])
def test_Assets_cycle_leadtime_error(config, hours):
with raises(UWError) as e:
ConcreteAssets(config=config, leadtime=dt.timedelta(hours=24))
ConcreteAssets(config=config, leadtime=dt.timedelta(hours=hours))
assert "When leadtime is specified, cycle is required" in str(e)


@pytest.mark.parametrize("val", (True, False))
def test_Asset_dry_run(config, val):
def test_Assets_dry_run(config, val):
with patch.object(driver, "dryrun") as dryrun:
ConcreteAssets(config=config, dry_run=val)
dryrun.assert_called_once_with(enable=val)
Expand All @@ -165,7 +166,7 @@ def test_key_path(config):
assert config == assetobj._config


def test_Asset_validate(caplog, assetobj):
def test_Assets_validate(assetobj, caplog):
log.setLevel(logging.INFO)
assetobj.validate()
assert regex_logged(caplog, "State: Ready")
Expand All @@ -183,8 +184,8 @@ def test_Asset_validate(caplog, assetobj):
(True, True, {"a": 33, "b": 22}),
],
)
def test_Asset__create_user_updated_config_base_file(
base_file, assetobj, expected, tmp_path, update_values
def test_Assets__create_user_updated_config_base_file(
assetobj, base_file, expected, tmp_path, update_values
):
path = tmp_path / "updated.yaml"
dc = assetobj._driver_config
Expand All @@ -198,14 +199,14 @@ def test_Asset__create_user_updated_config_base_file(
assert updated == expected


def test_Asset__driver_config_fail(assetobj):
def test_Assets__driver_config_fail(assetobj):
del assetobj._config["concrete"]
with raises(UWConfigError) as e:
assert assetobj._driver_config
assert str(e.value) == "Required 'concrete' block missing in config"


def test_Asset__driver_config_pass(assetobj):
def test_Assets__driver_config_pass(assetobj):
assert set(assetobj._driver_config.keys()) == {
"base_file",
"execution",
Expand All @@ -214,11 +215,11 @@ def test_Asset__driver_config_pass(assetobj):
}


def test_Asset__rundir(assetobj):
assert assetobj._rundir == Path("/path/to/2024032218/run")
def test_Assets__rundir(assetobj):
assert assetobj._rundir == Path(assetobj._driver_config["run_dir"])


def test_Asset__validate(assetobj):
def test_Assets__validate(assetobj):
with patch.object(assetobj, "_validate", driver.Assets._validate):
with patch.object(driver, "validate_internal") as validate_internal:
assetobj._validate(assetobj)
Expand Down
12 changes: 7 additions & 5 deletions src/uwtools/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,10 +545,11 @@ def test__dispatch_template_translate_no_optional():
)


def test__dispatch_to_driver():
@pytest.mark.parametrize("hours", [0, 24, 168])
def test__dispatch_to_driver(hours):
name = "adriver"
cycle = dt.datetime.now()
leadtime = dt.timedelta(hours=24)
leadtime = dt.timedelta(hours=hours)
args: dict = {
"action": "foo",
"batch": True,
Expand Down Expand Up @@ -646,9 +647,10 @@ def test__switch():


def test__timedelta_from_str(capsys):
assert cli._timedelta_from_str("11:12:13") == dt.timedelta(hours=11, minutes=12, seconds=13)
assert cli._timedelta_from_str("11:12") == dt.timedelta(hours=11, minutes=12)
assert cli._timedelta_from_str("11") == dt.timedelta(hours=11)
assert cli._timedelta_from_str("111:222:333").total_seconds() == 111 * 3600 + 222 * 60 + 333
assert cli._timedelta_from_str("111:222").total_seconds() == 111 * 3600 + 222 * 60
assert cli._timedelta_from_str("111").total_seconds() == 111 * 3600
assert cli._timedelta_from_str("01:15:07").total_seconds() == 1 * 3600 + 15 * 60 + 7
with raises(SystemExit):
cli._timedelta_from_str("foo")
assert f"Specify leadtime as {cli.LEADTIME_DESC}" in capsys.readouterr().err
Expand Down
29 changes: 14 additions & 15 deletions src/uwtools/tests/utils/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from unittest.mock import patch

import pytest
import yaml
from pytest import fixture, raises

from uwtools.exceptions import UWError
from uwtools.tests.drivers import test_driver
from uwtools.tests.drivers.test_driver import ConcreteDriver
from uwtools.utils import api

Expand Down Expand Up @@ -118,20 +118,19 @@ def test_str2path_convert():
assert result == Path(val)


def test__execute(execute_kwargs, tmp_path):
config = tmp_path / "config.yaml"
with open(config, "w", encoding="utf-8") as f:
yaml.dump({"some": "config"}, f)
@pytest.mark.parametrize("hours", [0, 24, 168])
def test__execute(execute_kwargs, hours, tmp_path):
graph_file = tmp_path / "g.dot"
kwargs = {
**execute_kwargs,
"driver_class": ConcreteDriver,
"config": config,
"cycle": dt.datetime.now(),
"leadtime": dt.timedelta(hours=24),
"graph_file": graph_file,
}
assert not graph_file.is_file()
with patch.object(ConcreteDriver, "_validate"):
with patch.object(test_driver, "ConcreteDriver", wraps=test_driver.ConcreteDriver) as cd:
kwargs = {
**execute_kwargs,
"driver_class": cd,
"config": {"some": "config"},
"cycle": dt.datetime.now(),
"leadtime": dt.timedelta(hours=hours),
"graph_file": graph_file,
}
assert not graph_file.is_file()
assert api._execute(**kwargs) is True
assert cd.call_args.kwargs["leadtime"] == dt.timedelta(hours=hours)
assert graph_file.is_file()
2 changes: 1 addition & 1 deletion src/uwtools/utils/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def _execute(
)
if cycle:
kwargs["cycle"] = cycle
if leadtime:
if leadtime is not None:
kwargs["leadtime"] = leadtime
obj = driver_class(**kwargs)
getattr(obj, task)()
Expand Down

0 comments on commit b08be17

Please sign in to comment.