From 7827f9ccaea272c37186fdd6630d6ab848bf07a1 Mon Sep 17 00:00:00 2001 From: Arkadii Yakovets Date: Fri, 18 Aug 2023 12:20:04 -0700 Subject: [PATCH] Update country `province` validation (#84463) * Update country `province` validation. * Run pre-commit. * Add tests * Mod config flow --------- Co-authored-by: G Johansson --- .../components/workday/binary_sensor.py | 26 +++++++++++-------- .../components/workday/config_flow.py | 15 ++++++----- tests/components/workday/__init__.py | 9 +++++++ .../components/workday/test_binary_sensor.py | 16 ++++++++++++ 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/workday/binary_sensor.py b/homeassistant/components/workday/binary_sensor.py index d1666fa9097..4c383543125 100644 --- a/homeassistant/components/workday/binary_sensor.py +++ b/homeassistant/components/workday/binary_sensor.py @@ -4,8 +4,13 @@ from __future__ import annotations from datetime import date, timedelta from typing import Any -import holidays -from holidays import DateLike, HolidayBase +from holidays import ( + DateLike, + HolidayBase, + __version__ as python_holidays_version, + country_holidays, + list_supported_countries, +) import voluptuous as vol from homeassistant.components.binary_sensor import ( @@ -43,7 +48,6 @@ from .const import ( def valid_country(value: Any) -> str: """Validate that the given country is supported.""" value = cv.string(value) - all_supported_countries = holidays.list_supported_countries() try: raw_value = value.encode("utf-8") @@ -53,7 +57,7 @@ def valid_country(value: Any) -> str: ) from err if not raw_value: raise vol.Invalid("Country name or the abbreviation must not be empty.") - if value not in all_supported_countries: + if value not in list_supported_countries(): raise vol.Invalid("Country is not supported.") return value @@ -123,17 +127,17 @@ async def async_setup_entry( province: str | None = entry.options.get(CONF_PROVINCE) sensor_name: str = entry.options[CONF_NAME] workdays: list[str] = entry.options[CONF_WORKDAYS] - - cls: HolidayBase = getattr(holidays, country) year: int = (dt_util.now() + timedelta(days=days_offset)).year - if province and province not in cls.subdivisions: + if country and country not in list_supported_countries(): + LOGGER.error("There is no country %s", country) + return + + if province and province not in list_supported_countries()[country]: LOGGER.error("There is no subdivision %s in country %s", province, country) return - obj_holidays = cls( - subdiv=province, years=year, language=cls.default_language - ) # type: ignore[operator] + obj_holidays: HolidayBase = country_holidays(country, subdiv=province, years=year) # Add custom holidays try: @@ -209,7 +213,7 @@ class IsWorkdaySensor(BinarySensorEntity): entry_type=DeviceEntryType.SERVICE, identifiers={(DOMAIN, entry_id)}, manufacturer="python-holidays", - model=holidays.__version__, + model=python_holidays_version, name=name, ) diff --git a/homeassistant/components/workday/config_flow.py b/homeassistant/components/workday/config_flow.py index 15e04ffca93..54c6196b75b 100644 --- a/homeassistant/components/workday/config_flow.py +++ b/homeassistant/components/workday/config_flow.py @@ -3,8 +3,7 @@ from __future__ import annotations from typing import Any -import holidays -from holidays import HolidayBase, list_supported_countries +from holidays import HolidayBase, country_holidays, list_supported_countries import voluptuous as vol from homeassistant.config_entries import ( @@ -77,12 +76,14 @@ def validate_custom_dates(user_input: dict[str, Any]) -> None: if dt_util.parse_date(add_date) is None: raise AddDatesError("Incorrect date") - cls: HolidayBase = getattr(holidays, user_input[CONF_COUNTRY]) + cls: HolidayBase = country_holidays(user_input[CONF_COUNTRY]) year: int = dt_util.now().year - - obj_holidays = cls( - subdiv=user_input.get(CONF_PROVINCE), years=year, language=cls.default_language - ) # type: ignore[operator] + obj_holidays: HolidayBase = country_holidays( + user_input[CONF_COUNTRY], + subdiv=user_input.get(CONF_PROVINCE), + years=year, + language=cls.default_language, + ) for remove_date in user_input[CONF_REMOVE_HOLIDAYS]: if dt_util.parse_date(remove_date) is None: diff --git a/tests/components/workday/__init__.py b/tests/components/workday/__init__.py index 005a63397d9..f87328998e1 100644 --- a/tests/components/workday/__init__.py +++ b/tests/components/workday/__init__.py @@ -50,6 +50,15 @@ TEST_CONFIG_WITH_PROVINCE = { "add_holidays": [], "remove_holidays": [], } +TEST_CONFIG_INCORRECT_COUNTRY = { + "name": DEFAULT_NAME, + "country": "ZZ", + "excludes": DEFAULT_EXCLUDES, + "days_offset": DEFAULT_OFFSET, + "workdays": DEFAULT_WORKDAYS, + "add_holidays": [], + "remove_holidays": [], +} TEST_CONFIG_INCORRECT_PROVINCE = { "name": DEFAULT_NAME, "country": "DE", diff --git a/tests/components/workday/test_binary_sensor.py b/tests/components/workday/test_binary_sensor.py index 71dd23c19a3..a8cea01a864 100644 --- a/tests/components/workday/test_binary_sensor.py +++ b/tests/components/workday/test_binary_sensor.py @@ -17,6 +17,7 @@ from . import ( TEST_CONFIG_EXAMPLE_2, TEST_CONFIG_INCLUDE_HOLIDAY, TEST_CONFIG_INCORRECT_ADD_REMOVE, + TEST_CONFIG_INCORRECT_COUNTRY, TEST_CONFIG_INCORRECT_PROVINCE, TEST_CONFIG_NO_PROVINCE, TEST_CONFIG_NO_STATE, @@ -187,6 +188,21 @@ async def test_setup_day_after_tomorrow( assert state.state == "off" +async def test_setup_faulty_country( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test setup with faulty province.""" + freezer.move_to(datetime(2017, 1, 6, 12, tzinfo=UTC)) # Friday + await init_integration(hass, TEST_CONFIG_INCORRECT_COUNTRY) + + state = hass.states.get("binary_sensor.workday_sensor") + assert state is None + + assert "There is no country" in caplog.text + + async def test_setup_faulty_province( hass: HomeAssistant, freezer: FrozenDateTimeFactory,