Skip to content

Commit

Permalink
Bring back typing check. Meanwhile just for homeassistant/*.py (#14410)
Browse files Browse the repository at this point in the history
* Bring back typing check. Meanwhile just for homeassistant/.py

* Change follow-imports to silent. Add a few more checks.
  • Loading branch information
andrey-git authored and balloob committed May 12, 2018
1 parent 70af7e5 commit 7aec098
Show file tree
Hide file tree
Showing 11 changed files with 41 additions and 30 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 3 additions & 2 deletions homeassistant/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
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 (
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
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
Expand Down
5 changes: 3 additions & 2 deletions homeassistant/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -297,7 +298,7 @@ async def async_stop_async_handler(event):
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.
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
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
Expand Down Expand Up @@ -60,7 +60,7 @@
(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:
Expand Down Expand Up @@ -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:
Expand Down
25 changes: 15 additions & 10 deletions homeassistant/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
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
Expand All @@ -41,6 +41,8 @@
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -136,13 +137,14 @@ def __init__(self, loop=None):
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)
Expand All @@ -152,13 +154,13 @@ def start(self) -> None:
# 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.
Expand Down Expand Up @@ -200,7 +202,10 @@ def add_job(self, target: Callable[..., None], *args: Any) -> None:
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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1039,7 +1044,7 @@ def __init__(self):
# 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.
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""The exceptions used by Home Assistant."""
import jinja2


class HomeAssistantError(Exception):
Expand All @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 6 additions & 4 deletions homeassistant/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,11 @@ def log_error(msg, link=True):

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)
Expand All @@ -165,14 +166,15 @@ def log_error(msg, link=True):
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
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
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):
Expand All @@ -40,8 +40,8 @@ def guard_func(*args, **kwargs):


# 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'


Expand Down
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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'

0 comments on commit 7aec098

Please sign in to comment.