From 67499e0204937db561123b00fcc370b1f0ce6e12 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 9 Feb 2023 13:33:10 +0100 Subject: [PATCH] Enable pydantic mypy plugin (#87415) --- .github/workflows/ci.yaml | 2 +- .../components/lametric/config_flow.py | 2 +- homeassistant/components/lametric/notify.py | 2 +- homeassistant/components/lametric/services.py | 7 +++--- mypy.ini | 7 ++++++ requirements_test.txt | 1 + script/hassfest/mypy_config.py | 22 ++++++++++++++++++- 7 files changed, 35 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b4384d68bc4..857b079d09d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -30,7 +30,7 @@ on: env: CACHE_VERSION: 5 PIP_CACHE_VERSION: 4 - MYPY_CACHE_VERSION: 3 + MYPY_CACHE_VERSION: 4 HA_SHORT_VERSION: 2023.3 DEFAULT_PYTHON: "3.10" ALL_PYTHON_VERSIONS: "['3.10']" diff --git a/homeassistant/components/lametric/config_flow.py b/homeassistant/components/lametric/config_flow.py index 4d4ebc15850..8e9da5851cf 100644 --- a/homeassistant/components/lametric/config_flow.py +++ b/homeassistant/components/lametric/config_flow.py @@ -255,7 +255,7 @@ class LaMetricFlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN): model=Model( cycles=2, frames=[Simple(text="Connected to Home Assistant!", icon=7956)], - sound=Sound(id=NotificationSound.WIN), + sound=Sound(sound=NotificationSound.WIN), ), ) ) diff --git a/homeassistant/components/lametric/notify.py b/homeassistant/components/lametric/notify.py index c9ae376c496..d8b9627238c 100644 --- a/homeassistant/components/lametric/notify.py +++ b/homeassistant/components/lametric/notify.py @@ -52,7 +52,7 @@ class LaMetricNotificationService(BaseNotificationService): sound = None if CONF_SOUND in data: - sound = Sound(id=data[CONF_SOUND], category=None) + sound = Sound(sound=data[CONF_SOUND], category=None) notification = Notification( icon_type=NotificationIconType(data.get(CONF_ICON_TYPE, "none")), diff --git a/homeassistant/components/lametric/services.py b/homeassistant/components/lametric/services.py index 2cbdfff6fd8..a20680267d9 100644 --- a/homeassistant/components/lametric/services.py +++ b/homeassistant/components/lametric/services.py @@ -1,11 +1,10 @@ """Support for LaMetric time services.""" from __future__ import annotations -from collections.abc import Sequence - from demetriek import ( AlarmSound, Chart, + Goal, LaMetricError, Model, Notification, @@ -113,12 +112,12 @@ def async_setup_services(hass: HomeAssistant) -> None: async def async_send_notification( coordinator: LaMetricDataUpdateCoordinator, call: ServiceCall, - frames: Sequence[Chart | Simple], + frames: list[Chart | Goal | Simple], ) -> None: """Send a notification to an LaMetric device.""" sound = None if CONF_SOUND in call.data: - sound = Sound(id=call.data[CONF_SOUND], category=None) + sound = Sound(sound=call.data[CONF_SOUND], category=None) notification = Notification( icon_type=NotificationIconType(call.data[CONF_ICON_TYPE]), diff --git a/mypy.ini b/mypy.ini index 5358346494c..f88fcea240c 100644 --- a/mypy.ini +++ b/mypy.ini @@ -4,6 +4,7 @@ [mypy] python_version = 3.10 +plugins = pydantic.mypy show_error_codes = true follow_imports = silent ignore_missing_imports = true @@ -26,6 +27,12 @@ disallow_untyped_defs = true warn_return_any = true warn_unreachable = true +[pydantic-mypy] +init_forbid_extra = true +init_typed = true +warn_required_dynamic_aliases = true +warn_untyped_fields = true + [mypy-homeassistant.*] no_implicit_reexport = true diff --git a/requirements_test.txt b/requirements_test.txt index a6b63c0fed0..7926e1d4194 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -14,6 +14,7 @@ freezegun==1.2.2 mock-open==1.4.0 mypy==1.0.0 pre-commit==3.0.0 +pydantic==1.10.4 pylint==2.16.0 pylint-per-file-ignores==1.1.0 pipdeptree==2.3.1 diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index 86565c9065c..c9b599bdcf0 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -37,6 +37,7 @@ HEADER: Final = """ GENERAL_SETTINGS: Final[dict[str, str]] = { "python_version": ".".join(str(x) for x in REQUIRED_PYTHON_VER[:2]), + "plugins": ", ".join(["pydantic.mypy"]), "show_error_codes": "true", "follow_imports": "silent", # Enable some checks globally. @@ -84,6 +85,18 @@ STRICT_SETTINGS_CORE: Final[list[str]] = [ "disallow_any_generics", ] +# Plugin specific settings +# Bump mypy cache when updating! Some plugins don't invalidate the cache properly. +# pydantic: https://docs.pydantic.dev/mypy_plugin/#plugin-settings +PLUGIN_CONFIG: Final[dict[str, dict[str, str]]] = { + "pydantic-mypy": { + "init_forbid_extra": "true", + "init_typed": "true", + "warn_required_dynamic_aliases": "true", + "warn_untyped_fields": "true", + } +} + def _strict_module_in_ignore_list( module: str, ignored_modules_set: set[str] @@ -132,7 +145,7 @@ def _generate_and_validate_strict_typing(config: Config) -> str: return "\n".join(_sort_within_sections(lines)) + "\n" -def _generate_and_validate_mypy_config(config: Config) -> str: +def _generate_and_validate_mypy_config(config: Config) -> str: # noqa: C901 """Validate and generate mypy config.""" # Filter empty and commented lines. @@ -199,6 +212,13 @@ def _generate_and_validate_mypy_config(config: Config) -> str: for key in STRICT_SETTINGS: mypy_config.set(general_section, key, "true") + for plugin_name, plugin_config in PLUGIN_CONFIG.items(): + if not plugin_config: + continue + mypy_config.add_section(plugin_name) + for key, value in plugin_config.items(): + mypy_config.set(plugin_name, key, value) + # By default enable no_implicit_reexport only for homeassistant.* # Disable it afterwards for all components components_section = "mypy-homeassistant.*"