Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add typing to homeassistant/*.py and homeassistant/util/ #15569

Merged
merged 5 commits into from
Jul 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions homeassistant/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
)


def attempt_use_uvloop():
def attempt_use_uvloop() -> None:
"""Attempt to use uvloop."""
import asyncio

Expand Down Expand Up @@ -280,8 +280,8 @@ def setup_and_run_hass(config_dir: str,
# Imported here to avoid importing asyncio before monkey patch
from homeassistant.util.async_ import run_callback_threadsafe

def open_browser(event):
"""Open the webinterface in a browser."""
def open_browser(_: Any) -> None:
"""Open the web interface in a browser."""
if hass.config.api is not None: # type: ignore
import webbrowser
webbrowser.open(hass.config.api.base_url) # type: ignore
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ async def async_from_config_file(config_path: str,
@core.callback
def async_enable_logging(hass: core.HomeAssistant,
verbose: bool = False,
log_rotate_days=None,
log_file=None,
log_rotate_days: Optional[int] = None,
log_file: Optional[str] = None,
log_no_color: bool = False) -> None:
"""Set up the logging.

Expand Down Expand Up @@ -291,7 +291,7 @@ def async_enable_logging(hass: core.HomeAssistant,

async_handler = AsyncHandler(hass.loop, err_handler)

async def async_stop_async_handler(event):
async def async_stop_async_handler(_: Any) -> None:
"""Cleanup async handler."""
logging.getLogger('').removeHandler(async_handler) # type: ignore
await async_handler.async_close(blocking=True)
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/rachio.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from aiohttp import web
import voluptuous as vol

from typing import Optional
from homeassistant.auth.util import generate_secret
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import CONF_API_KEY, EVENT_HOMEASSISTANT_STOP, URL_API
Expand Down Expand Up @@ -242,7 +242,7 @@ def list_zones(self, include_disabled=False) -> list:
# Only enabled zones
return [z for z in self._zones if z[KEY_ENABLED]]

def get_zone(self, zone_id) -> dict or None:
def get_zone(self, zone_id) -> Optional[dict]:
"""Return the zone with the given ID."""
for zone in self.list_zones(include_disabled=True):
if zone[KEY_ID] == zone_id:
Expand Down
85 changes: 48 additions & 37 deletions homeassistant/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
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

Expand All @@ -21,7 +22,7 @@
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
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -276,31 +277,34 @@ 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
configuration by itself.

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.
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -430,11 +436,10 @@ def set_time_zone(time_zone_str):
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]))
Expand Down Expand Up @@ -484,12 +489,12 @@ def set_time_zone(time_zone_str):
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:
Expand All @@ -515,7 +520,7 @@ def set_time_zone(time_zone_str):

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))
Expand All @@ -526,7 +531,8 @@ def set_time_zone(time_zone_str):
", ".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)
Expand All @@ -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)
Expand All @@ -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:
Expand All @@ -577,8 +585,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)
Expand Down Expand Up @@ -642,7 +650,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.
Expand Down Expand Up @@ -704,14 +713,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:
Expand All @@ -720,7 +729,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.
Expand Down
12 changes: 7 additions & 5 deletions homeassistant/config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ async def async_step_discovery(info):

import logging
import uuid
from typing import Set # noqa pylint: disable=unused-import
from typing import Set, Optional # noqa pylint: disable=unused-import

from homeassistant import data_entry_flow
from homeassistant.core import callback
from homeassistant.core import callback, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component, async_process_deps_reqs
from homeassistant.util.decorator import Registry
Expand Down Expand Up @@ -164,8 +164,9 @@ class ConfigEntry:
__slots__ = ('entry_id', 'version', 'domain', 'title', 'data', 'source',
'state')

def __init__(self, version, domain, title, data, source, entry_id=None,
state=ENTRY_STATE_NOT_LOADED):
def __init__(self, version: str, domain: str, title: str, data: dict,
source: str, entry_id: Optional[str] = None,
state: str = ENTRY_STATE_NOT_LOADED) -> None:
"""Initialize a config entry."""
# Unique id of the config entry
self.entry_id = entry_id or uuid.uuid4().hex
Expand All @@ -188,7 +189,8 @@ def __init__(self, version, domain, title, data, source, entry_id=None,
# State of the entry (LOADED, NOT_LOADED)
self.state = state

async def async_setup(self, hass, *, component=None):
async def async_setup(
self, hass: HomeAssistant, *, component=None) -> None:
"""Set up an entry."""
if component is None:
component = getattr(hass.components, self.domain)
Expand Down
Loading