Allow float values in time periods (#38023)

This commit is contained in:
Phil Bruckner 2020-07-21 19:41:42 -05:00 committed by GitHub
parent 4a5a09a0e9
commit 726d5fdd94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 26 deletions

View File

@ -86,7 +86,7 @@ import homeassistant.util.dt as dt_util
# pylint: disable=invalid-name # pylint: disable=invalid-name
TIME_PERIOD_ERROR = "offset {} should be format 'HH:MM' or 'HH:MM:SS'" TIME_PERIOD_ERROR = "offset {} should be format 'HH:MM', 'HH:MM:SS' or 'HH:MM:SS.F'"
# Home Assistant types # Home Assistant types
byte = vol.All(vol.Coerce(int), vol.Range(min=0, max=255)) byte = vol.All(vol.Coerce(int), vol.Range(min=0, max=255))
@ -299,11 +299,11 @@ time_period_dict = vol.All(
dict, dict,
vol.Schema( vol.Schema(
{ {
"days": vol.Coerce(int), "days": vol.Coerce(float),
"hours": vol.Coerce(int), "hours": vol.Coerce(float),
"minutes": vol.Coerce(int), "minutes": vol.Coerce(float),
"seconds": vol.Coerce(int), "seconds": vol.Coerce(float),
"milliseconds": vol.Coerce(int), "milliseconds": vol.Coerce(float),
} }
), ),
has_at_least_one_key("days", "hours", "minutes", "seconds", "milliseconds"), has_at_least_one_key("days", "hours", "minutes", "seconds", "milliseconds"),
@ -357,17 +357,17 @@ def time_period_str(value: str) -> timedelta:
elif value.startswith("+"): elif value.startswith("+"):
value = value[1:] value = value[1:]
try: parsed = value.split(":")
parsed = [int(x) for x in value.split(":")] if len(parsed) not in (2, 3):
except ValueError:
raise vol.Invalid(TIME_PERIOD_ERROR.format(value)) raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
try:
if len(parsed) == 2: hour = int(parsed[0])
hour, minute = parsed minute = int(parsed[1])
try:
second = float(parsed[2])
except IndexError:
second = 0 second = 0
elif len(parsed) == 3: except ValueError:
hour, minute, second = parsed
else:
raise vol.Invalid(TIME_PERIOD_ERROR.format(value)) raise vol.Invalid(TIME_PERIOD_ERROR.format(value))
offset = timedelta(hours=hour, minutes=minute, seconds=second) offset = timedelta(hours=hour, minutes=minute, seconds=second)
@ -378,10 +378,10 @@ def time_period_str(value: str) -> timedelta:
return offset return offset
def time_period_seconds(value: Union[int, str]) -> timedelta: def time_period_seconds(value: Union[float, str]) -> timedelta:
"""Validate and transform seconds to a time offset.""" """Validate and transform seconds to a time offset."""
try: try:
return timedelta(seconds=int(value)) return timedelta(seconds=float(value))
except (ValueError, TypeError): except (ValueError, TypeError):
raise vol.Invalid(f"Expected seconds, got {value}") raise vol.Invalid(f"Expected seconds, got {value}")

View File

@ -260,18 +260,49 @@ def test_time_period():
"""Test time_period validation.""" """Test time_period validation."""
schema = vol.Schema(cv.time_period) schema = vol.Schema(cv.time_period)
options = (None, "", "hello:world", "12:", "12:34:56:78", {}, {"wrong_key": -10}) options = (
None,
"",
"hello:world",
"12:",
"12:34:56:78",
{},
{"wrong_key": -10},
"12.5:30",
"12:30.5",
"12.5:30:30",
"12:30.5:30",
)
for value in options: for value in options:
with pytest.raises(vol.MultipleInvalid): with pytest.raises(vol.MultipleInvalid):
schema(value) schema(value)
options = ("8:20", "23:59", "-8:20", "-23:59:59", "-48:00", {"minutes": 5}, 1, "5") options = (
for value in options: ("8:20", timedelta(hours=8, minutes=20)),
schema(value) ("23:59", timedelta(hours=23, minutes=59)),
("-8:20", -1 * timedelta(hours=8, minutes=20)),
assert timedelta(seconds=180) == schema("180") ("-1:15", -1 * timedelta(hours=1, minutes=15)),
assert timedelta(hours=23, minutes=59) == schema("23:59") ("-23:59:59", -1 * timedelta(hours=23, minutes=59, seconds=59)),
assert -1 * timedelta(hours=1, minutes=15) == schema("-1:15") ("-48:00", -1 * timedelta(days=2)),
({"minutes": 5}, timedelta(minutes=5)),
(1, timedelta(seconds=1)),
("5", timedelta(seconds=5)),
("180", timedelta(seconds=180)),
("00:08:20.5", timedelta(minutes=8, seconds=20, milliseconds=500)),
("00:23:59.999", timedelta(minutes=23, seconds=59, milliseconds=999)),
("-00:08:20.5", -1 * timedelta(minutes=8, seconds=20, milliseconds=500)),
(
"-12:59:59.999",
-1 * timedelta(hours=12, minutes=59, seconds=59, milliseconds=999),
),
({"milliseconds": 1.5}, timedelta(milliseconds=1, microseconds=500)),
({"seconds": "1.5"}, timedelta(seconds=1, milliseconds=500)),
({"minutes": "1.5"}, timedelta(minutes=1, seconds=30)),
({"hours": -1.5}, -1 * timedelta(hours=1, minutes=30)),
({"days": "-1.5"}, -1 * timedelta(days=1, hours=12)),
)
for value, result in options:
assert schema(value) == result
def test_remove_falsy(): def test_remove_falsy():