Bring back typing check. Meanwhile just for homeassistant/*.py (#14410)

* Bring back typing check. Meanwhile just for homeassistant/.py

* Change follow-imports to silent. Add a few more checks.
This commit is contained in:
Andrey 2018-05-13 00:44:53 +03:00 committed by Paulus Schoutsen
parent 70af7e5fad
commit 7aec098a05
11 changed files with 41 additions and 30 deletions

View file

@ -10,8 +10,8 @@ matrix:
env: TOXENV=lint
- python: "3.5.3"
env: TOXENV=pylint
# - python: "3.5"
# env: TOXENV=typing
- python: "3.5.3"
env: TOXENV=typing
- python: "3.5.3"
env: TOXENV=py35
- python: "3.6"

View file

@ -8,7 +8,8 @@ import subprocess
import sys
import threading
from typing import Optional, List
from typing import Optional, List, Dict, Any # noqa #pylint: disable=unused-import
from homeassistant import monkey_patch
from homeassistant.const import (
@ -259,7 +260,7 @@ def setup_and_run_hass(config_dir: str,
config = {
'frontend': {},
'demo': {}
}
} # type: Dict[str, Any]
hass = bootstrap.from_config_dict(
config, config_dir=config_dir, verbose=args.verbose,
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days,

View file

@ -35,7 +35,7 @@ ACCESS_TOKEN_EXPIRATION = timedelta(minutes=30)
DATA_REQS = 'auth_reqs_processed'
def generate_secret(entropy=32):
def generate_secret(entropy: int = 32) -> str:
"""Generate a secret.
Backport of secrets.token_hex from Python 3.6

View file

@ -278,7 +278,8 @@ def async_enable_logging(hass: core.HomeAssistant,
if log_rotate_days:
err_handler = logging.handlers.TimedRotatingFileHandler(
err_log_path, when='midnight', backupCount=log_rotate_days)
err_log_path, when='midnight',
backupCount=log_rotate_days) # type: logging.FileHandler
else:
err_handler = logging.FileHandler(
err_log_path, mode='w', delay=True)
@ -297,7 +298,7 @@ def async_enable_logging(hass: core.HomeAssistant,
EVENT_HOMEASSISTANT_CLOSE, async_stop_async_handler)
logger = logging.getLogger('')
logger.addHandler(async_handler)
logger.addHandler(async_handler) # type: ignore
logger.setLevel(logging.INFO)
# Save the log file location for access by other components.

View file

@ -7,7 +7,7 @@ import os
import re
import shutil
# pylint: disable=unused-import
from typing import Any, List, Tuple # NOQA
from typing import Any, List, Tuple, Optional # NOQA
import voluptuous as vol
from voluptuous.humanize import humanize_error
@ -60,7 +60,7 @@ DEFAULT_CORE_CONFIG = (
(CONF_TIME_ZONE, 'UTC', 'time_zone', 'Pick yours from here: http://en.wiki'
'pedia.org/wiki/List_of_tz_database_time_zones'),
(CONF_CUSTOMIZE, '!include customize.yaml', None, 'Customization file'),
) # type: Tuple[Tuple[str, Any, Any, str], ...]
) # type: Tuple[Tuple[str, Any, Any, Optional[str]], ...]
DEFAULT_CONFIG = """
# Show links to resources in log and frontend
introduction:
@ -167,7 +167,7 @@ def get_default_config_dir() -> str:
"""Put together the default configuration directory based on the OS."""
data_dir = os.getenv('APPDATA') if os.name == "nt" \
else os.path.expanduser('~')
return os.path.join(data_dir, CONFIG_DIR_NAME)
return os.path.join(data_dir, CONFIG_DIR_NAME) # type: ignore
def ensure_config_exists(config_dir: str, detect_location: bool = True) -> str:

View file

@ -17,7 +17,7 @@ import threading
from time import monotonic
from types import MappingProxyType
from typing import Optional, Any, Callable, List # NOQA
from typing import Optional, Any, Callable, List, TypeVar, Dict # NOQA
from async_timeout import timeout
import voluptuous as vol
@ -41,6 +41,8 @@ import homeassistant.util.dt as dt_util
import homeassistant.util.location as location
from homeassistant.util.unit_system import UnitSystem, METRIC_SYSTEM # NOQA
T = TypeVar('T')
DOMAIN = 'homeassistant'
# How long we wait for the result of a service call
@ -70,16 +72,15 @@ def valid_state(state: str) -> bool:
return len(state) < 256
def callback(func: Callable[..., None]) -> Callable[..., None]:
def callback(func: Callable[..., T]) -> Callable[..., T]:
"""Annotation to mark method as safe to call from within the event loop."""
# pylint: disable=protected-access
func._hass_callback = True
setattr(func, '_hass_callback', True)
return func
def is_callback(func: Callable[..., Any]) -> bool:
"""Check if function is safe to be called in the event loop."""
return '_hass_callback' in getattr(func, '__dict__', {})
return getattr(func, '_hass_callback', False) is True
@callback
@ -136,13 +137,14 @@ class HomeAssistant(object):
self.data = {}
self.state = CoreState.not_running
self.exit_code = None
self.config_entries = None
@property
def is_running(self) -> bool:
"""Return if Home Assistant is running."""
return self.state in (CoreState.starting, CoreState.running)
def start(self) -> None:
def start(self) -> int:
"""Start home assistant."""
# Register the async start
fire_coroutine_threadsafe(self.async_start(), self.loop)
@ -152,13 +154,13 @@ class HomeAssistant(object):
# Block until stopped
_LOGGER.info("Starting Home Assistant core loop")
self.loop.run_forever()
return self.exit_code
except KeyboardInterrupt:
self.loop.call_soon_threadsafe(
self.loop.create_task, self.async_stop())
self.loop.run_forever()
finally:
self.loop.close()
return self.exit_code
async def async_start(self):
"""Finalize startup from inside the event loop.
@ -200,7 +202,10 @@ class HomeAssistant(object):
self.loop.call_soon_threadsafe(self.async_add_job, target, *args)
@callback
def async_add_job(self, target: Callable[..., None], *args: Any) -> None:
def async_add_job(
self,
target: Callable[..., Any],
*args: Any) -> Optional[asyncio.tasks.Task]:
"""Add a job from within the eventloop.
This method must be run in the event loop.
@ -354,7 +359,7 @@ class EventBus(object):
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize a new event bus."""
self._listeners = {}
self._listeners = {} # type: Dict[str, List[Callable]]
self._hass = hass
@callback
@ -1039,7 +1044,7 @@ class Config(object):
# List of allowed external dirs to access
self.whitelist_external_dirs = set()
def distance(self: object, lat: float, lon: float) -> float:
def distance(self, lat: float, lon: float) -> float:
"""Calculate distance from Home Assistant.
Async friendly.

View file

@ -1,4 +1,5 @@
"""The exceptions used by Home Assistant."""
import jinja2
class HomeAssistantError(Exception):
@ -22,7 +23,7 @@ class NoEntitySpecifiedError(HomeAssistantError):
class TemplateError(HomeAssistantError):
"""Error during template rendering."""
def __init__(self, exception):
def __init__(self, exception: jinja2.TemplateError) -> None:
"""Init the error."""
super().__init__('{}: {}'.format(exception.__class__.__name__,
exception))

View file

@ -93,7 +93,7 @@ def get_component(hass, comp_or_platform) -> Optional[ModuleType]:
# This prevents that when only
# custom_components/switch/some_platform.py exists,
# the import custom_components.switch would succeed.
if module.__spec__.origin == 'namespace':
if module.__spec__ and module.__spec__.origin == 'namespace':
continue
_LOGGER.info("Loaded %s from %s", comp_or_platform, path)

View file

@ -139,10 +139,11 @@ async def _async_setup_component(hass: core.HomeAssistant,
try:
if hasattr(component, 'async_setup'):
result = await component.async_setup(hass, processed_config)
result = await component.async_setup( # type: ignore
hass, processed_config)
else:
result = await hass.async_add_job(
component.setup, hass, processed_config)
component.setup, hass, processed_config) # type: ignore
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Error during setup of component %s", domain)
async_notify_setup_error(hass, domain, True)
@ -165,14 +166,15 @@ async def _async_setup_component(hass: core.HomeAssistant,
for entry in hass.config_entries.async_entries(domain):
await entry.async_setup(hass, component=component)
hass.config.components.add(component.DOMAIN)
hass.config.components.add(component.DOMAIN) # type: ignore
# Cleanup
if domain in hass.data[DATA_SETUP]:
hass.data[DATA_SETUP].pop(domain)
hass.bus.async_fire(
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}
EVENT_COMPONENT_LOADED,
{ATTR_COMPONENT: component.DOMAIN} # type: ignore
)
return True

View file

@ -24,7 +24,7 @@ logging.basicConfig(level=logging.INFO)
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
def test_real(func):
def check_real(func):
"""Force a function to require a keyword _test_real to be passed in."""
@functools.wraps(func)
def guard_func(*args, **kwargs):
@ -40,8 +40,8 @@ def test_real(func):
# Guard a few functions that would make network connections
location.detect_location_info = test_real(location.detect_location_info)
location.elevation = test_real(location.elevation)
location.detect_location_info = check_real(location.detect_location_info)
location.elevation = check_real(location.elevation)
util.get_local_ip = lambda: '127.0.0.1'

View file

@ -38,7 +38,8 @@ commands =
[testenv:typing]
basepython = {env:PYTHON3_PATH:python3}
whitelist_externals=/bin/bash
deps =
-r{toxinidir}/requirements_test.txt
commands =
mypy --ignore-missing-imports --follow-imports=skip homeassistant
/bin/bash -c 'mypy --ignore-missing-imports --follow-imports=silent homeassistant/*.py'