Add typing to homeassistant/*.py and homeassistant/util/ (#15569)
* Add typing to homeassistant/*.py and homeassistant/util/ * Fix wrong merge * Restore iterable in OrderedSet * Fix tests
This commit is contained in:
parent
b7c336a687
commit
140a874917
27 changed files with 532 additions and 384 deletions
|
@ -7,8 +7,9 @@ import os
|
|||
import re
|
||||
import shutil
|
||||
# pylint: disable=unused-import
|
||||
from typing import Any, Tuple, Optional # noqa: F401
|
||||
|
||||
from typing import ( # noqa: F401
|
||||
Any, Tuple, Optional, Dict, List, Union, Callable)
|
||||
from types import ModuleType
|
||||
import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
|
||||
|
@ -21,7 +22,7 @@ from homeassistant.const import (
|
|||
CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT, TEMP_CELSIUS,
|
||||
__version__, CONF_CUSTOMIZE, CONF_CUSTOMIZE_DOMAIN, CONF_CUSTOMIZE_GLOB,
|
||||
CONF_WHITELIST_EXTERNAL_DIRS, CONF_AUTH_PROVIDERS, CONF_TYPE)
|
||||
from homeassistant.core import callback, DOMAIN as CONF_CORE
|
||||
from homeassistant.core import callback, DOMAIN as CONF_CORE, HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.loader import get_component, get_platform
|
||||
from homeassistant.util.yaml import load_yaml, SECRET_YAML
|
||||
|
@ -193,7 +194,7 @@ def ensure_config_exists(config_dir: str, detect_location: bool = True)\
|
|||
return config_path
|
||||
|
||||
|
||||
def create_default_config(config_dir: str, detect_location=True)\
|
||||
def create_default_config(config_dir: str, detect_location: bool = True)\
|
||||
-> Optional[str]:
|
||||
"""Create a default configuration file in given configuration directory.
|
||||
|
||||
|
@ -276,7 +277,7 @@ def create_default_config(config_dir: str, detect_location=True)\
|
|||
return None
|
||||
|
||||
|
||||
async def async_hass_config_yaml(hass):
|
||||
async def async_hass_config_yaml(hass: HomeAssistant) -> Dict:
|
||||
"""Load YAML from a Home Assistant configuration file.
|
||||
|
||||
This function allow a component inside the asyncio loop to reload its
|
||||
|
@ -284,23 +285,26 @@ async def async_hass_config_yaml(hass):
|
|||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
def _load_hass_yaml_config():
|
||||
def _load_hass_yaml_config() -> Dict:
|
||||
path = find_config_file(hass.config.config_dir)
|
||||
conf = load_yaml_config_file(path)
|
||||
return conf
|
||||
if path is None:
|
||||
raise HomeAssistantError(
|
||||
"Config file not found in: {}".format(hass.config.config_dir))
|
||||
return load_yaml_config_file(path)
|
||||
|
||||
conf = await hass.async_add_job(_load_hass_yaml_config)
|
||||
return conf
|
||||
return await hass.async_add_executor_job(_load_hass_yaml_config)
|
||||
|
||||
|
||||
def find_config_file(config_dir: str) -> Optional[str]:
|
||||
def find_config_file(config_dir: Optional[str]) -> Optional[str]:
|
||||
"""Look in given directory for supported configuration files."""
|
||||
if config_dir is None:
|
||||
return None
|
||||
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
||||
|
||||
return config_path if os.path.isfile(config_path) else None
|
||||
|
||||
|
||||
def load_yaml_config_file(config_path):
|
||||
def load_yaml_config_file(config_path: str) -> Dict[Any, Any]:
|
||||
"""Parse a YAML configuration file.
|
||||
|
||||
This method needs to run in an executor.
|
||||
|
@ -323,7 +327,7 @@ def load_yaml_config_file(config_path):
|
|||
return conf_dict
|
||||
|
||||
|
||||
def process_ha_config_upgrade(hass):
|
||||
def process_ha_config_upgrade(hass: HomeAssistant) -> None:
|
||||
"""Upgrade configuration if necessary.
|
||||
|
||||
This method needs to run in an executor.
|
||||
|
@ -360,7 +364,8 @@ def process_ha_config_upgrade(hass):
|
|||
|
||||
|
||||
@callback
|
||||
def async_log_exception(ex, domain, config, hass):
|
||||
def async_log_exception(ex: vol.Invalid, domain: str, config: Dict,
|
||||
hass: HomeAssistant) -> None:
|
||||
"""Log an error for configuration validation.
|
||||
|
||||
This method must be run in the event loop.
|
||||
|
@ -371,7 +376,7 @@ def async_log_exception(ex, domain, config, hass):
|
|||
|
||||
|
||||
@callback
|
||||
def _format_config_error(ex, domain, config):
|
||||
def _format_config_error(ex: vol.Invalid, domain: str, config: Dict) -> str:
|
||||
"""Generate log exception for configuration validation.
|
||||
|
||||
This method must be run in the event loop.
|
||||
|
@ -396,7 +401,8 @@ def _format_config_error(ex, domain, config):
|
|||
return message
|
||||
|
||||
|
||||
async def async_process_ha_core_config(hass, config):
|
||||
async def async_process_ha_core_config(
|
||||
hass: HomeAssistant, config: Dict) -> None:
|
||||
"""Process the [homeassistant] section from the configuration.
|
||||
|
||||
This method is a coroutine.
|
||||
|
@ -405,12 +411,12 @@ async def async_process_ha_core_config(hass, config):
|
|||
|
||||
# Only load auth during startup.
|
||||
if not hasattr(hass, 'auth'):
|
||||
hass.auth = await auth.auth_manager_from_config(
|
||||
hass, config.get(CONF_AUTH_PROVIDERS, []))
|
||||
setattr(hass, 'auth', await auth.auth_manager_from_config(
|
||||
hass, config.get(CONF_AUTH_PROVIDERS, [])))
|
||||
|
||||
hac = hass.config
|
||||
|
||||
def set_time_zone(time_zone_str):
|
||||
def set_time_zone(time_zone_str: Optional[str]) -> None:
|
||||
"""Help to set the time zone."""
|
||||
if time_zone_str is None:
|
||||
return
|
||||
|
@ -430,11 +436,10 @@ async def async_process_ha_core_config(hass, config):
|
|||
if key in config:
|
||||
setattr(hac, attr, config[key])
|
||||
|
||||
if CONF_TIME_ZONE in config:
|
||||
set_time_zone(config.get(CONF_TIME_ZONE))
|
||||
set_time_zone(config.get(CONF_TIME_ZONE))
|
||||
|
||||
# Init whitelist external dir
|
||||
hac.whitelist_external_dirs = set((hass.config.path('www'),))
|
||||
hac.whitelist_external_dirs = {hass.config.path('www')}
|
||||
if CONF_WHITELIST_EXTERNAL_DIRS in config:
|
||||
hac.whitelist_external_dirs.update(
|
||||
set(config[CONF_WHITELIST_EXTERNAL_DIRS]))
|
||||
|
@ -484,12 +489,12 @@ async def async_process_ha_core_config(hass, config):
|
|||
hac.time_zone, hac.elevation):
|
||||
return
|
||||
|
||||
discovered = []
|
||||
discovered = [] # type: List[Tuple[str, Any]]
|
||||
|
||||
# If we miss some of the needed values, auto detect them
|
||||
if None in (hac.latitude, hac.longitude, hac.units,
|
||||
hac.time_zone):
|
||||
info = await hass.async_add_job(
|
||||
info = await hass.async_add_executor_job(
|
||||
loc_util.detect_location_info)
|
||||
|
||||
if info is None:
|
||||
|
@ -515,7 +520,7 @@ async def async_process_ha_core_config(hass, config):
|
|||
|
||||
if hac.elevation is None and hac.latitude is not None and \
|
||||
hac.longitude is not None:
|
||||
elevation = await hass.async_add_job(
|
||||
elevation = await hass.async_add_executor_job(
|
||||
loc_util.elevation, hac.latitude, hac.longitude)
|
||||
hac.elevation = elevation
|
||||
discovered.append(('elevation', elevation))
|
||||
|
@ -526,7 +531,8 @@ async def async_process_ha_core_config(hass, config):
|
|||
", ".join('{}: {}'.format(key, val) for key, val in discovered))
|
||||
|
||||
|
||||
def _log_pkg_error(package, component, config, message):
|
||||
def _log_pkg_error(
|
||||
package: str, component: str, config: Dict, message: str) -> None:
|
||||
"""Log an error while merging packages."""
|
||||
message = "Package {} setup failed. Component {} {}".format(
|
||||
package, component, message)
|
||||
|
@ -539,12 +545,13 @@ def _log_pkg_error(package, component, config, message):
|
|||
_LOGGER.error(message)
|
||||
|
||||
|
||||
def _identify_config_schema(module):
|
||||
def _identify_config_schema(module: ModuleType) -> \
|
||||
Tuple[Optional[str], Optional[Dict]]:
|
||||
"""Extract the schema and identify list or dict based."""
|
||||
try:
|
||||
schema = module.CONFIG_SCHEMA.schema[module.DOMAIN]
|
||||
schema = module.CONFIG_SCHEMA.schema[module.DOMAIN] # type: ignore
|
||||
except (AttributeError, KeyError):
|
||||
return (None, None)
|
||||
return None, None
|
||||
t_schema = str(schema)
|
||||
if t_schema.startswith('{'):
|
||||
return ('dict', schema)
|
||||
|
@ -553,9 +560,10 @@ def _identify_config_schema(module):
|
|||
return '', schema
|
||||
|
||||
|
||||
def _recursive_merge(conf, package):
|
||||
def _recursive_merge(
|
||||
conf: Dict[str, Any], package: Dict[str, Any]) -> Union[bool, str]:
|
||||
"""Merge package into conf, recursively."""
|
||||
error = False
|
||||
error = False # type: Union[bool, str]
|
||||
for key, pack_conf in package.items():
|
||||
if isinstance(pack_conf, dict):
|
||||
if not pack_conf:
|
||||
|
@ -576,8 +584,8 @@ def _recursive_merge(conf, package):
|
|||
return error
|
||||
|
||||
|
||||
def merge_packages_config(hass, config, packages,
|
||||
_log_pkg_error=_log_pkg_error):
|
||||
def merge_packages_config(hass: HomeAssistant, config: Dict, packages: Dict,
|
||||
_log_pkg_error: Callable = _log_pkg_error) -> Dict:
|
||||
"""Merge packages into the top-level configuration. Mutate config."""
|
||||
# pylint: disable=too-many-nested-blocks
|
||||
PACKAGES_CONFIG_SCHEMA(packages)
|
||||
|
@ -641,7 +649,8 @@ def merge_packages_config(hass, config, packages,
|
|||
|
||||
|
||||
@callback
|
||||
def async_process_component_config(hass, config, domain):
|
||||
def async_process_component_config(
|
||||
hass: HomeAssistant, config: Dict, domain: str) -> Optional[Dict]:
|
||||
"""Check component configuration and return processed configuration.
|
||||
|
||||
Returns None on error.
|
||||
|
@ -703,14 +712,14 @@ def async_process_component_config(hass, config, domain):
|
|||
return config
|
||||
|
||||
|
||||
async def async_check_ha_config_file(hass):
|
||||
async def async_check_ha_config_file(hass: HomeAssistant) -> Optional[str]:
|
||||
"""Check if Home Assistant configuration file is valid.
|
||||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
from homeassistant.scripts.check_config import check_ha_config_file
|
||||
|
||||
res = await hass.async_add_job(
|
||||
res = await hass.async_add_executor_job(
|
||||
check_ha_config_file, hass)
|
||||
|
||||
if not res.errors:
|
||||
|
@ -719,7 +728,9 @@ async def async_check_ha_config_file(hass):
|
|||
|
||||
|
||||
@callback
|
||||
def async_notify_setup_error(hass, component, display_link=False):
|
||||
def async_notify_setup_error(
|
||||
hass: HomeAssistant, component: str,
|
||||
display_link: bool = False) -> None:
|
||||
"""Print a persistent notification.
|
||||
|
||||
This method must be run in the event loop.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue