From d64f1e767c5320f7b134a33164ec5a34e41f4a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Sun, 21 Jul 2019 19:59:02 +0300 Subject: [PATCH] Type check all helpers (#25373) * Type check all helpers, add inline exclusions for work in progress * Remove unused Script._template_cache * Add some missing type hints * Remove unneeded type: ignore * Type hint fixes * Mypy assistance tweaks * Don't look for None in deprecated config "at most once" check * Avoid None name slugify attempt when generating entity id * Avoid None state store attempt on entity remove --- homeassistant/helpers/check_config.py | 21 ++++++++++------- homeassistant/helpers/config_entry_flow.py | 2 ++ homeassistant/helpers/config_validation.py | 27 ++++++++++++++-------- homeassistant/helpers/data_entry_flow.py | 2 ++ homeassistant/helpers/device_registry.py | 4 ++++ homeassistant/helpers/discovery.py | 3 +++ homeassistant/helpers/entity.py | 24 +++++++++++-------- homeassistant/helpers/entity_component.py | 3 +++ homeassistant/helpers/entity_platform.py | 3 +++ homeassistant/helpers/entity_registry.py | 7 +++++- homeassistant/helpers/event.py | 10 +++++--- homeassistant/helpers/logging.py | 2 ++ homeassistant/helpers/restore_state.py | 10 ++++++-- homeassistant/helpers/script.py | 21 ++++++++++------- homeassistant/helpers/service.py | 3 +++ homeassistant/helpers/storage.py | 12 ++++++---- homeassistant/helpers/template.py | 11 +++++---- homeassistant/util/json.py | 4 ++-- mypyrc | 19 +-------------- 19 files changed, 119 insertions(+), 69 deletions(-) diff --git a/homeassistant/helpers/check_config.py b/homeassistant/helpers/check_config.py index c1de7d3b459..efd693b77ab 100644 --- a/homeassistant/helpers/check_config.py +++ b/homeassistant/helpers/check_config.py @@ -1,6 +1,6 @@ """Helper to check the configuration file.""" from collections import OrderedDict, namedtuple -# from typing import Dict, List, Sequence +from typing import List import attr import voluptuous as vol @@ -17,6 +17,9 @@ import homeassistant.util.yaml.loader as yaml_loader from homeassistant.exceptions import HomeAssistantError +# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs +# mypy: no-warn-return-any + CheckConfigError = namedtuple( 'CheckConfigError', "message domain config") @@ -25,7 +28,8 @@ CheckConfigError = namedtuple( class HomeAssistantConfig(OrderedDict): """Configuration result with errors attribute.""" - errors = attr.ib(default=attr.Factory(list)) + errors = attr.ib( + default=attr.Factory(list)) # type: List[CheckConfigError] def add_error(self, message, domain=None, config=None): """Add a single error.""" @@ -114,9 +118,10 @@ async def async_check_ha_config_file(hass: HomeAssistant) -> \ result.add_error("Component not found: {}".format(domain)) continue - if hasattr(component, 'CONFIG_SCHEMA'): + config_schema = getattr(component, 'CONFIG_SCHEMA', None) + if config_schema is not None: try: - config = component.CONFIG_SCHEMA(config) + config = config_schema(config) result[domain] = config[domain] except vol.Invalid as ex: _comp_error(ex, domain, config) @@ -133,8 +138,7 @@ async def async_check_ha_config_file(hass: HomeAssistant) -> \ for p_name, p_config in config_per_platform(config, domain): # Validate component specific platform schema try: - p_validated = component_platform_schema( # type: ignore - p_config) + p_validated = component_platform_schema(p_config) except vol.Invalid as ex: _comp_error(ex, domain, config) continue @@ -163,9 +167,10 @@ async def async_check_ha_config_file(hass: HomeAssistant) -> \ continue # Validate platform specific schema - if hasattr(platform, 'PLATFORM_SCHEMA'): + platform_schema = getattr(platform, 'PLATFORM_SCHEMA', None) + if platform_schema is not None: try: - p_validated = platform.PLATFORM_SCHEMA(p_validated) + p_validated = platform_schema(p_validated) except vol.Invalid as ex: _comp_error( ex, '{}.{}'.format(domain, p_name), p_validated) diff --git a/homeassistant/helpers/config_entry_flow.py b/homeassistant/helpers/config_entry_flow.py index c3e5195131b..70a9af54d04 100644 --- a/homeassistant/helpers/config_entry_flow.py +++ b/homeassistant/helpers/config_entry_flow.py @@ -4,6 +4,8 @@ from functools import partial from homeassistant import config_entries +# mypy: allow-incomplete-defs, allow-untyped-defs + def register_discovery_flow(domain, title, discovery_function, connection_class): """Register flow for discovered integrations that not require auth.""" diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 60457a9963c..51bbaed0ff4 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -27,6 +27,9 @@ from homeassistant.exceptions import TemplateError from homeassistant.helpers.logging import KeywordStyleAdapter from homeassistant.util import slugify as util_slugify + +# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs +# mypy: no-check-untyped-defs, no-warn-return-any # pylint: disable=invalid-name TIME_PERIOD_ERROR = "offset {} should be format 'HH:MM' or 'HH:MM:SS'" @@ -92,7 +95,8 @@ def boolean(value: Any) -> bool: if value in ('0', 'false', 'no', 'off', 'disable'): return False elif isinstance(value, Number): - return value != 0 + # type ignore: https://github.com/python/mypy/issues/3186 + return value != 0 # type: ignore raise vol.Invalid('invalid boolean value {}'.format(value)) @@ -161,7 +165,7 @@ def isdir(value: Any) -> str: return dir_in -def ensure_list(value: Union[T, Sequence[T]]) -> Sequence[T]: +def ensure_list(value: Union[T, Sequence[T], None]) -> Sequence[T]: """Wrap value in list if it is not one.""" if value is None: return [] @@ -556,7 +560,8 @@ def deprecated(key: str, else: # Unclear when it is None, but it happens, so let's guard. # https://github.com/home-assistant/home-assistant/issues/24982 - module_name = __name__ + # type ignore/unreachable: https://github.com/python/typeshed/pull/3137 + module_name = __name__ # type: ignore if replacement_key and invalidation_version: warning = ("The '{key}' option (with value '{value}') is" @@ -606,13 +611,15 @@ def deprecated(key: str, config.pop(key) else: value = default - if (replacement_key - and (replacement_key not in config - or default == config.get(replacement_key)) - and value is not None): - config[replacement_key] = value + keys = [key] + if replacement_key: + keys.append(replacement_key) + if value is not None and ( + replacement_key not in config or + default == config.get(replacement_key)): + config[replacement_key] = value - return has_at_most_one_key(key, replacement_key)(config) + return has_at_most_one_key(*keys)(config) return validator @@ -739,7 +746,7 @@ CONDITION_SCHEMA = vol.Any( ZONE_CONDITION_SCHEMA, AND_CONDITION_SCHEMA, OR_CONDITION_SCHEMA, -) +) # type: vol.Schema _SCRIPT_DELAY_SCHEMA = vol.Schema({ vol.Optional(CONF_ALIAS): string, diff --git a/homeassistant/helpers/data_entry_flow.py b/homeassistant/helpers/data_entry_flow.py index d3ac4763269..10cfebe8542 100644 --- a/homeassistant/helpers/data_entry_flow.py +++ b/homeassistant/helpers/data_entry_flow.py @@ -7,6 +7,8 @@ from homeassistant.components.http import HomeAssistantView from homeassistant.components.http.data_validator import RequestDataValidator +# mypy: allow-untyped-calls, allow-untyped-defs + class _BaseFlowManagerView(HomeAssistantView): """Foundation for flow manager views.""" diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index 7a056060167..f901a205cd1 100644 --- a/homeassistant/helpers/device_registry.py +++ b/homeassistant/helpers/device_registry.py @@ -12,6 +12,10 @@ from homeassistant.loader import bind_hass from .typing import HomeAssistantType + +# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs +# mypy: no-check-untyped-defs, no-warn-return-any + _LOGGER = logging.getLogger(__name__) _UNDEF = object() diff --git a/homeassistant/helpers/discovery.py b/homeassistant/helpers/discovery.py index 0c547166d1a..f3ef4193f7d 100644 --- a/homeassistant/helpers/discovery.py +++ b/homeassistant/helpers/discovery.py @@ -13,6 +13,9 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.loader import DEPENDENCY_BLACKLIST from homeassistant.util.async_ import run_callback_threadsafe + +# mypy: allow-untyped-defs, no-check-untyped-defs + EVENT_LOAD_PLATFORM = 'load_platform.{}' ATTR_PLATFORM = 'platform' diff --git a/homeassistant/helpers/entity.py b/homeassistant/helpers/entity.py index f039cc315e3..220a8b6c8fc 100644 --- a/homeassistant/helpers/entity.py +++ b/homeassistant/helpers/entity.py @@ -10,14 +10,19 @@ from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, DEVICE_DEFAULT_NAME, STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT, ATTR_ENTITY_PICTURE, ATTR_SUPPORTED_FEATURES, ATTR_DEVICE_CLASS) -from homeassistant.helpers.entity_registry import EVENT_ENTITY_REGISTRY_UPDATED -from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_registry import ( + EVENT_ENTITY_REGISTRY_UPDATED, RegistryEntry) +from homeassistant.core import HomeAssistant, callback, CALLBACK_TYPE from homeassistant.config import DATA_CUSTOMIZE from homeassistant.exceptions import NoEntitySpecifiedError from homeassistant.util import ensure_unique_string, slugify from homeassistant.util.async_ import run_callback_threadsafe from homeassistant.util import dt as dt_util + +# mypy: allow-incomplete-defs, allow-untyped-defs, no-check-untyped-defs +# mypy: no-warn-return-any + _LOGGER = logging.getLogger(__name__) SLOW_UPDATE_WARNING = 10 @@ -34,7 +39,7 @@ def generate_entity_id(entity_id_format: str, name: Optional[str], current_ids, hass ).result() - name = (slugify(name) or slugify(DEVICE_DEFAULT_NAME)).lower() + name = (slugify(name or "") or slugify(DEVICE_DEFAULT_NAME)).lower() return ensure_unique_string( entity_id_format.format(name), current_ids) @@ -80,10 +85,10 @@ class Entity: parallel_updates = None # Entry in the entity registry - registry_entry = None + registry_entry = None # type: Optional[RegistryEntry] # Hold list for functions to call on remove. - _on_remove = None + _on_remove = None # type: Optional[List[CALLBACK_TYPE]] # Context _context = None @@ -98,7 +103,7 @@ class Entity: return True @property - def unique_id(self) -> str: + def unique_id(self) -> Optional[str]: """Return a unique ID.""" return None @@ -137,7 +142,7 @@ class Entity: return None @property - def device_class(self) -> str: + def device_class(self) -> Optional[str]: """Return the class of this device, from component DEVICE_CLASSES.""" return None @@ -181,7 +186,7 @@ class Entity: return False @property - def supported_features(self) -> int: + def supported_features(self) -> Optional[int]: """Flag supported features.""" return None @@ -386,7 +391,7 @@ class Entity: self.parallel_updates.release() @callback - def async_on_remove(self, func): + def async_on_remove(self, func: CALLBACK_TYPE) -> None: """Add a function to call when entity removed.""" if self._on_remove is None: self._on_remove = [] @@ -421,6 +426,7 @@ class Entity: Not to be extended by integrations. """ if self.registry_entry is not None: + assert self.hass is not None self.async_on_remove(self.hass.bus.async_listen( EVENT_ENTITY_REGISTRY_UPDATED, self._async_registry_updated)) diff --git a/homeassistant/helpers/entity_component.py b/homeassistant/helpers/entity_component.py index fb31e664605..fb8c6c4e4ac 100644 --- a/homeassistant/helpers/entity_component.py +++ b/homeassistant/helpers/entity_component.py @@ -17,6 +17,9 @@ from homeassistant.loader import bind_hass, async_get_integration from homeassistant.util import slugify from .entity_platform import EntityPlatform + +# mypy: allow-untyped-defs, no-check-untyped-defs + DEFAULT_SCAN_INTERVAL = timedelta(seconds=15) DATA_INSTANCES = 'entity_components' diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index 12e0e01acaf..d58ead0682c 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -9,6 +9,9 @@ from homeassistant.util.async_ import ( from .event import async_track_time_interval, async_call_later + +# mypy: allow-untyped-defs, no-check-untyped-defs + SLOW_SETUP_WARNING = 10 SLOW_SETUP_MAX_WAIT = 60 PLATFORM_NOT_READY_RETRIES = 10 diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index 6d3a8a42655..934d866d9dc 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -23,6 +23,10 @@ from homeassistant.util.yaml import load_yaml from .typing import HomeAssistantType + +# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs +# mypy: no-check-untyped-defs, no-warn-return-any + PATH_REGISTRY = 'entity_registry.yaml' DATA_REGISTRY = 'entity_registry' EVENT_ENTITY_REGISTRY_UPDATED = 'entity_registry_updated' @@ -48,7 +52,8 @@ class RegistryEntry: config_entry_id = attr.ib(type=str, default=None) disabled_by = attr.ib( type=str, default=None, - validator=attr.validators.in_((DISABLED_HASS, DISABLED_USER, None))) + validator=attr.validators.in_((DISABLED_HASS, DISABLED_USER, None)) + ) # type: Optional[str] domain = attr.ib(type=str, init=False, repr=False) @domain.default diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 009c2b1e898..5292948ab6c 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -7,13 +7,16 @@ import attr from homeassistant.loader import bind_hass from homeassistant.helpers.sun import get_astral_event_next -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import HomeAssistant, callback, CALLBACK_TYPE from homeassistant.const import ( ATTR_NOW, EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL, SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET, EVENT_CORE_CONFIG_UPDATE) from homeassistant.util import dt as dt_util from homeassistant.util.async_ import run_callback_threadsafe + +# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs +# mypy: no-check-untyped-defs, no-warn-return-any # PyLint does not like the use of threaded_listener_factory # pylint: disable=invalid-name @@ -172,7 +175,7 @@ track_same_state = threaded_listener_factory(async_track_same_state) @callback @bind_hass -def async_track_point_in_time(hass, action, point_in_time): +def async_track_point_in_time(hass, action, point_in_time) -> CALLBACK_TYPE: """Add a listener that fires once after a specific point in time.""" utc_point_in_time = dt_util.as_utc(point_in_time) @@ -190,7 +193,8 @@ track_point_in_time = threaded_listener_factory(async_track_point_in_time) @callback @bind_hass -def async_track_point_in_utc_time(hass, action, point_in_time): +def async_track_point_in_utc_time( + hass, action, point_in_time) -> CALLBACK_TYPE: """Add a listener that fires once after a specific point in UTC time.""" # Ensure point_in_time is UTC point_in_time = dt_util.as_utc(point_in_time) diff --git a/homeassistant/helpers/logging.py b/homeassistant/helpers/logging.py index ea596eb3c15..784a28ea324 100644 --- a/homeassistant/helpers/logging.py +++ b/homeassistant/helpers/logging.py @@ -3,6 +3,8 @@ import inspect import logging +# mypy: allow-untyped-defs, no-check-untyped-defs + class KeywordMessage: """ Represents a logging message with keyword arguments. diff --git a/homeassistant/helpers/restore_state.py b/homeassistant/helpers/restore_state.py index 26b10882d09..3645ea09627 100644 --- a/homeassistant/helpers/restore_state.py +++ b/homeassistant/helpers/restore_state.py @@ -15,6 +15,10 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.storage import Store # noqa pylint_disable=unused-import + +# mypy: allow-untyped-calls, allow-untyped-defs, no-check-untyped-defs +# mypy: no-warn-return-any + DATA_RESTORE_STATE_TASK = 'restore_state_task' _LOGGER = logging.getLogger(__name__) @@ -182,8 +186,8 @@ class RestoreStateData(): # we're going to serialize it to JSON and then re-load it. if state is not None: state = State.from_dict(_encode_complex(state.as_dict())) - - self.last_states[entity_id] = StoredState(state, dt_util.utcnow()) + if state is not None: + self.last_states[entity_id] = StoredState(state, dt_util.utcnow()) self.entity_ids.remove(entity_id) @@ -219,6 +223,7 @@ class RestoreEntity(Entity): async def async_internal_added_to_hass(self) -> None: """Register this entity as a restorable entity.""" + assert self.hass is not None _, data = await asyncio.gather( super().async_internal_added_to_hass(), RestoreStateData.async_get_instance(self.hass), @@ -227,6 +232,7 @@ class RestoreEntity(Entity): async def async_internal_will_remove_from_hass(self) -> None: """Run when entity will be removed from hass.""" + assert self.hass is not None _, data = await asyncio.gather( super().async_internal_will_remove_from_hass(), RestoreStateData.async_get_instance(self.hass), diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index e4693c3cd3b..3c0e8e584d0 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -2,12 +2,13 @@ import logging from contextlib import suppress +from datetime import datetime from itertools import islice -from typing import Optional, Sequence +from typing import Optional, Sequence, Callable, Dict, List, Set, Tuple import voluptuous as vol -from homeassistant.core import HomeAssistant, Context, callback +from homeassistant.core import HomeAssistant, Context, callback, CALLBACK_TYPE from homeassistant.const import CONF_CONDITION, CONF_TIMEOUT from homeassistant import exceptions from homeassistant.helpers import ( @@ -20,6 +21,10 @@ import homeassistant.util.dt as date_util from homeassistant.util.async_ import ( run_coroutine_threadsafe, run_callback_threadsafe) + +# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs +# mypy: no-check-untyped-defs + _LOGGER = logging.getLogger(__name__) CONF_ALIAS = 'alias' @@ -76,7 +81,8 @@ class _SuspendScript(Exception): class Script(): """Representation of a script.""" - def __init__(self, hass: HomeAssistant, sequence, name: str = None, + def __init__(self, hass: HomeAssistant, sequence, + name: Optional[str] = None, change_listener=None) -> None: """Initialize the script.""" self.hass = hass @@ -85,14 +91,13 @@ class Script(): self.name = name self._change_listener = change_listener self._cur = -1 - self._exception_step = None + self._exception_step = None # type: Optional[int] self.last_action = None - self.last_triggered = None + self.last_triggered = None # type: Optional[datetime] self.can_cancel = any(CONF_DELAY in action or CONF_WAIT_TEMPLATE in action for action in self.sequence) - self._async_listener = [] - self._template_cache = {} - self._config_cache = {} + self._async_listener = [] # type: List[CALLBACK_TYPE] + self._config_cache = {} # type: Dict[Set[Tuple], Callable[..., bool]] self._actions = { ACTION_DELAY: self._async_delay, ACTION_WAIT_TEMPLATE: self._async_wait_template, diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index a09e552348c..8b04bd90921 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -19,6 +19,9 @@ import homeassistant.helpers.config_validation as cv from homeassistant.util.async_ import run_coroutine_threadsafe from homeassistant.helpers.typing import HomeAssistantType + +# mypy: allow-incomplete-defs, allow-untyped-defs, no-check-untyped-defs + CONF_SERVICE = 'service' CONF_SERVICE_TEMPLATE = 'service_template' CONF_SERVICE_ENTITY_ID = 'entity_id' diff --git a/homeassistant/helpers/storage.py b/homeassistant/helpers/storage.py index 67ce2f7a923..6c2af8f77b0 100644 --- a/homeassistant/helpers/storage.py +++ b/homeassistant/helpers/storage.py @@ -3,7 +3,7 @@ import asyncio from json import JSONEncoder import logging import os -from typing import Dict, List, Optional, Callable, Union +from typing import Dict, List, Optional, Callable, Union, Any, Type from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import callback @@ -11,6 +11,10 @@ from homeassistant.loader import bind_hass from homeassistant.util import json as json_util from homeassistant.helpers.event import async_call_later + +# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs +# mypy: no-warn-return-any + STORAGE_DIR = '.storage' _LOGGER = logging.getLogger(__name__) @@ -48,17 +52,17 @@ class Store: """Class to help storing data.""" def __init__(self, hass, version: int, key: str, private: bool = False, *, - encoder: JSONEncoder = None): + encoder: Optional[Type[JSONEncoder]] = None): """Initialize storage class.""" self.version = version self.key = key self.hass = hass self._private = private - self._data = None + self._data = None # type: Optional[Dict[str, Any]] self._unsub_delay_listener = None self._unsub_stop_listener = None self._write_lock = asyncio.Lock() - self._load_task = None + self._load_task = None # type: Optional[asyncio.Future] self._encoder = encoder @property diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 55db75642f4..6149f28b50b 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -12,7 +12,7 @@ from typing import Iterable import jinja2 from jinja2 import contextfilter, contextfunction from jinja2.sandbox import ImmutableSandboxedEnvironment -from jinja2.utils import Namespace +from jinja2.utils import Namespace # type: ignore from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_LATITUDE, ATTR_LONGITUDE, ATTR_UNIT_OF_MEASUREMENT, @@ -26,6 +26,10 @@ from homeassistant.loader import bind_hass from homeassistant.util import convert, dt as dt_util, location as loc_util from homeassistant.util.async_ import run_callback_threadsafe + +# mypy: allow-incomplete-defs, allow-untyped-calls, allow-untyped-defs +# mypy: no-check-untyped-defs, no-warn-return-any + _LOGGER = logging.getLogger(__name__) _SENTINEL = object() DATE_STR_FORMAT = "%Y-%m-%d %H:%M:%S" @@ -192,14 +196,13 @@ class Template: This method must be run in the event loop. """ - if self._compiled is None: - self._ensure_compiled() + compiled = self._compiled or self._ensure_compiled() if variables is not None: kwargs.update(variables) try: - return self._compiled.render(kwargs).strip() + return compiled.render(kwargs).strip() except jinja2.TemplateError as err: raise TemplateError(err) diff --git a/homeassistant/util/json.py b/homeassistant/util/json.py index 8ca1c702b6c..ce4a2291599 100644 --- a/homeassistant/util/json.py +++ b/homeassistant/util/json.py @@ -1,6 +1,6 @@ """JSON utility functions.""" import logging -from typing import Union, List, Dict, Optional +from typing import Union, List, Dict, Optional, Type import json import os @@ -42,7 +42,7 @@ def load_json(filename: str, default: Union[List, Dict, None] = None) \ def save_json(filename: str, data: Union[List, Dict], private: bool = False, *, - encoder: Optional[json.JSONEncoder] = None) -> None: + encoder: Optional[Type[json.JSONEncoder]] = None) -> None: """Save JSON data to a file. Returns True on success. diff --git a/mypyrc b/mypyrc index 7c73d12e381..0129a21a23f 100644 --- a/mypyrc +++ b/mypyrc @@ -1,21 +1,4 @@ homeassistant/*.py homeassistant/auth/ +homeassistant/helpers/ homeassistant/util/ -homeassistant/helpers/__init__.py -homeassistant/helpers/aiohttp_client.py -homeassistant/helpers/area_registry.py -homeassistant/helpers/condition.py -homeassistant/helpers/deprecation.py -homeassistant/helpers/dispatcher.py -homeassistant/helpers/entity_values.py -homeassistant/helpers/entityfilter.py -homeassistant/helpers/icon.py -homeassistant/helpers/intent.py -homeassistant/helpers/json.py -homeassistant/helpers/location.py -homeassistant/helpers/signal.py -homeassistant/helpers/state.py -homeassistant/helpers/sun.py -homeassistant/helpers/temperature.py -homeassistant/helpers/translation.py -homeassistant/helpers/typing.py