Improve boolean validator (#24294)

* Improve boolean validator

* Remove extra throw

* Remove None test as discussed

* Fix for tests depending on None == False
This commit is contained in:
Penny Wood 2019-06-08 13:18:02 +08:00 committed by Paulus Schoutsen
parent 233bc1a108
commit b30f4b8fc0
5 changed files with 20 additions and 9 deletions

View file

@ -6,6 +6,7 @@ import re
from datetime import (timedelta, datetime as datetime_sys, from datetime import (timedelta, datetime as datetime_sys,
time as time_sys, date as date_sys) time as time_sys, date as date_sys)
from socket import _GLOBAL_DEFAULT_TIMEOUT from socket import _GLOBAL_DEFAULT_TIMEOUT
from numbers import Number
from typing import Any, Union, TypeVar, Callable, Sequence, Dict, Optional from typing import Any, Union, TypeVar, Callable, Sequence, Dict, Optional
from urllib.parse import urlparse from urllib.parse import urlparse
from uuid import UUID from uuid import UUID
@ -81,14 +82,17 @@ def has_at_most_one_key(*keys: str) -> Callable:
def boolean(value: Any) -> bool: def boolean(value: Any) -> bool:
"""Validate and coerce a boolean value.""" """Validate and coerce a boolean value."""
if isinstance(value, bool):
return value
if isinstance(value, str): if isinstance(value, str):
value = value.lower() value = value.lower().strip()
if value in ('1', 'true', 'yes', 'on', 'enable'): if value in ('1', 'true', 'yes', 'on', 'enable'):
return True return True
if value in ('0', 'false', 'no', 'off', 'disable'): if value in ('0', 'false', 'no', 'off', 'disable'):
return False return False
elif isinstance(value, Number):
return value != 0
raise vol.Invalid('invalid boolean value {}'.format(value)) raise vol.Invalid('invalid boolean value {}'.format(value))
return bool(value)
def isdevice(value): def isdevice(value):

View file

@ -203,6 +203,7 @@ class TestDemoClimate(unittest.TestCase):
"""Test setting the away mode without required attribute.""" """Test setting the away mode without required attribute."""
state = self.hass.states.get(ENTITY_CLIMATE) state = self.hass.states.get(ENTITY_CLIMATE)
assert 'on' == state.attributes.get('away_mode') assert 'on' == state.attributes.get('away_mode')
with pytest.raises(vol.Invalid):
common.set_away_mode(self.hass, None, ENTITY_CLIMATE) common.set_away_mode(self.hass, None, ENTITY_CLIMATE)
self.hass.block_till_done() self.hass.block_till_done()
assert 'on' == state.attributes.get('away_mode') assert 'on' == state.attributes.get('away_mode')
@ -246,6 +247,7 @@ class TestDemoClimate(unittest.TestCase):
"""Test setting the auxiliary heater without required attribute.""" """Test setting the auxiliary heater without required attribute."""
state = self.hass.states.get(ENTITY_CLIMATE) state = self.hass.states.get(ENTITY_CLIMATE)
assert 'off' == state.attributes.get('aux_heat') assert 'off' == state.attributes.get('aux_heat')
with pytest.raises(vol.Invalid):
common.set_aux_heat(self.hass, None, ENTITY_CLIMATE) common.set_aux_heat(self.hass, None, ENTITY_CLIMATE)
self.hass.block_till_done() self.hass.block_till_done()
assert 'off' == state.attributes.get('aux_heat') assert 'off' == state.attributes.get('aux_heat')

View file

@ -90,6 +90,7 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert False is state.attributes.get('is_volume_muted') assert False is state.attributes.get('is_volume_muted')
with pytest.raises(vol.Invalid):
common.mute_volume(self.hass, None, entity_id) common.mute_volume(self.hass, None, entity_id)
self.hass.block_till_done() self.hass.block_till_done()
state = self.hass.states.get(entity_id) state = self.hass.states.get(entity_id)

View file

@ -95,6 +95,7 @@ class TestDemowater_heater(unittest.TestCase):
"""Test setting the away mode without required attribute.""" """Test setting the away mode without required attribute."""
state = self.hass.states.get(ENTITY_WATER_HEATER) state = self.hass.states.get(ENTITY_WATER_HEATER)
assert 'off' == state.attributes.get('away_mode') assert 'off' == state.attributes.get('away_mode')
with pytest.raises(vol.Invalid):
common.set_away_mode(self.hass, None, ENTITY_WATER_HEATER) common.set_away_mode(self.hass, None, ENTITY_WATER_HEATER)
self.hass.block_till_done() self.hass.block_till_done()
assert 'off' == state.attributes.get('away_mode') assert 'off' == state.attributes.get('away_mode')

View file

@ -17,11 +17,14 @@ def test_boolean():
"""Test boolean validation.""" """Test boolean validation."""
schema = vol.Schema(cv.boolean) schema = vol.Schema(cv.boolean)
for value in ('T', 'negative', 'lock'): for value in (
None, 'T', 'negative', 'lock', 'tr ue',
[], [1, 2], {'one': 'two'}, test_boolean):
with pytest.raises(vol.MultipleInvalid): with pytest.raises(vol.MultipleInvalid):
schema(value) schema(value)
for value in ('true', 'On', '1', 'YES', 'enable', 1, True): for value in ('true', 'On', '1', 'YES', ' true ',
'enable', 1, 50, True, 0.1):
assert schema(value) assert schema(value)
for value in ('false', 'Off', '0', 'NO', 'disable', 0, False): for value in ('false', 'Off', '0', 'NO', 'disable', 0, False):