mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
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:
parent
70af7e5fad
commit
7aec098a05
@ -10,8 +10,8 @@ matrix:
|
|||||||
env: TOXENV=lint
|
env: TOXENV=lint
|
||||||
- python: "3.5.3"
|
- python: "3.5.3"
|
||||||
env: TOXENV=pylint
|
env: TOXENV=pylint
|
||||||
# - python: "3.5"
|
- python: "3.5.3"
|
||||||
# env: TOXENV=typing
|
env: TOXENV=typing
|
||||||
- python: "3.5.3"
|
- python: "3.5.3"
|
||||||
env: TOXENV=py35
|
env: TOXENV=py35
|
||||||
- python: "3.6"
|
- python: "3.6"
|
||||||
|
@ -8,7 +8,8 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import threading
|
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 import monkey_patch
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -259,7 +260,7 @@ def setup_and_run_hass(config_dir: str,
|
|||||||
config = {
|
config = {
|
||||||
'frontend': {},
|
'frontend': {},
|
||||||
'demo': {}
|
'demo': {}
|
||||||
}
|
} # type: Dict[str, Any]
|
||||||
hass = bootstrap.from_config_dict(
|
hass = bootstrap.from_config_dict(
|
||||||
config, config_dir=config_dir, verbose=args.verbose,
|
config, config_dir=config_dir, verbose=args.verbose,
|
||||||
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days,
|
skip_pip=args.skip_pip, log_rotate_days=args.log_rotate_days,
|
||||||
|
@ -35,7 +35,7 @@ ACCESS_TOKEN_EXPIRATION = timedelta(minutes=30)
|
|||||||
DATA_REQS = 'auth_reqs_processed'
|
DATA_REQS = 'auth_reqs_processed'
|
||||||
|
|
||||||
|
|
||||||
def generate_secret(entropy=32):
|
def generate_secret(entropy: int = 32) -> str:
|
||||||
"""Generate a secret.
|
"""Generate a secret.
|
||||||
|
|
||||||
Backport of secrets.token_hex from Python 3.6
|
Backport of secrets.token_hex from Python 3.6
|
||||||
|
@ -278,7 +278,8 @@ def async_enable_logging(hass: core.HomeAssistant,
|
|||||||
|
|
||||||
if log_rotate_days:
|
if log_rotate_days:
|
||||||
err_handler = logging.handlers.TimedRotatingFileHandler(
|
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:
|
else:
|
||||||
err_handler = logging.FileHandler(
|
err_handler = logging.FileHandler(
|
||||||
err_log_path, mode='w', delay=True)
|
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)
|
EVENT_HOMEASSISTANT_CLOSE, async_stop_async_handler)
|
||||||
|
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
logger.addHandler(async_handler)
|
logger.addHandler(async_handler) # type: ignore
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
# Save the log file location for access by other components.
|
# Save the log file location for access by other components.
|
||||||
|
@ -7,7 +7,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from typing import Any, List, Tuple # NOQA
|
from typing import Any, List, Tuple, Optional # NOQA
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from voluptuous.humanize import humanize_error
|
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'
|
(CONF_TIME_ZONE, 'UTC', 'time_zone', 'Pick yours from here: http://en.wiki'
|
||||||
'pedia.org/wiki/List_of_tz_database_time_zones'),
|
'pedia.org/wiki/List_of_tz_database_time_zones'),
|
||||||
(CONF_CUSTOMIZE, '!include customize.yaml', None, 'Customization file'),
|
(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 = """
|
DEFAULT_CONFIG = """
|
||||||
# Show links to resources in log and frontend
|
# Show links to resources in log and frontend
|
||||||
introduction:
|
introduction:
|
||||||
@ -167,7 +167,7 @@ def get_default_config_dir() -> str:
|
|||||||
"""Put together the default configuration directory based on the OS."""
|
"""Put together the default configuration directory based on the OS."""
|
||||||
data_dir = os.getenv('APPDATA') if os.name == "nt" \
|
data_dir = os.getenv('APPDATA') if os.name == "nt" \
|
||||||
else os.path.expanduser('~')
|
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:
|
def ensure_config_exists(config_dir: str, detect_location: bool = True) -> str:
|
||||||
|
@ -17,7 +17,7 @@ import threading
|
|||||||
from time import monotonic
|
from time import monotonic
|
||||||
|
|
||||||
from types import MappingProxyType
|
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
|
from async_timeout import timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -41,6 +41,8 @@ import homeassistant.util.dt as dt_util
|
|||||||
import homeassistant.util.location as location
|
import homeassistant.util.location as location
|
||||||
from homeassistant.util.unit_system import UnitSystem, METRIC_SYSTEM # NOQA
|
from homeassistant.util.unit_system import UnitSystem, METRIC_SYSTEM # NOQA
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
DOMAIN = 'homeassistant'
|
DOMAIN = 'homeassistant'
|
||||||
|
|
||||||
# How long we wait for the result of a service call
|
# 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
|
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."""
|
"""Annotation to mark method as safe to call from within the event loop."""
|
||||||
# pylint: disable=protected-access
|
setattr(func, '_hass_callback', True)
|
||||||
func._hass_callback = True
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
||||||
def is_callback(func: Callable[..., Any]) -> bool:
|
def is_callback(func: Callable[..., Any]) -> bool:
|
||||||
"""Check if function is safe to be called in the event loop."""
|
"""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
|
@callback
|
||||||
@ -136,13 +137,14 @@ class HomeAssistant(object):
|
|||||||
self.data = {}
|
self.data = {}
|
||||||
self.state = CoreState.not_running
|
self.state = CoreState.not_running
|
||||||
self.exit_code = None
|
self.exit_code = None
|
||||||
|
self.config_entries = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_running(self) -> bool:
|
def is_running(self) -> bool:
|
||||||
"""Return if Home Assistant is running."""
|
"""Return if Home Assistant is running."""
|
||||||
return self.state in (CoreState.starting, CoreState.running)
|
return self.state in (CoreState.starting, CoreState.running)
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> int:
|
||||||
"""Start home assistant."""
|
"""Start home assistant."""
|
||||||
# Register the async start
|
# Register the async start
|
||||||
fire_coroutine_threadsafe(self.async_start(), self.loop)
|
fire_coroutine_threadsafe(self.async_start(), self.loop)
|
||||||
@ -152,13 +154,13 @@ class HomeAssistant(object):
|
|||||||
# Block until stopped
|
# Block until stopped
|
||||||
_LOGGER.info("Starting Home Assistant core loop")
|
_LOGGER.info("Starting Home Assistant core loop")
|
||||||
self.loop.run_forever()
|
self.loop.run_forever()
|
||||||
return self.exit_code
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self.loop.call_soon_threadsafe(
|
self.loop.call_soon_threadsafe(
|
||||||
self.loop.create_task, self.async_stop())
|
self.loop.create_task, self.async_stop())
|
||||||
self.loop.run_forever()
|
self.loop.run_forever()
|
||||||
finally:
|
finally:
|
||||||
self.loop.close()
|
self.loop.close()
|
||||||
|
return self.exit_code
|
||||||
|
|
||||||
async def async_start(self):
|
async def async_start(self):
|
||||||
"""Finalize startup from inside the event loop.
|
"""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)
|
self.loop.call_soon_threadsafe(self.async_add_job, target, *args)
|
||||||
|
|
||||||
@callback
|
@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.
|
"""Add a job from within the eventloop.
|
||||||
|
|
||||||
This method must be run in the event loop.
|
This method must be run in the event loop.
|
||||||
@ -354,7 +359,7 @@ class EventBus(object):
|
|||||||
|
|
||||||
def __init__(self, hass: HomeAssistant) -> None:
|
def __init__(self, hass: HomeAssistant) -> None:
|
||||||
"""Initialize a new event bus."""
|
"""Initialize a new event bus."""
|
||||||
self._listeners = {}
|
self._listeners = {} # type: Dict[str, List[Callable]]
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@ -1039,7 +1044,7 @@ class Config(object):
|
|||||||
# List of allowed external dirs to access
|
# List of allowed external dirs to access
|
||||||
self.whitelist_external_dirs = set()
|
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.
|
"""Calculate distance from Home Assistant.
|
||||||
|
|
||||||
Async friendly.
|
Async friendly.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
"""The exceptions used by Home Assistant."""
|
"""The exceptions used by Home Assistant."""
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantError(Exception):
|
class HomeAssistantError(Exception):
|
||||||
@ -22,7 +23,7 @@ class NoEntitySpecifiedError(HomeAssistantError):
|
|||||||
class TemplateError(HomeAssistantError):
|
class TemplateError(HomeAssistantError):
|
||||||
"""Error during template rendering."""
|
"""Error during template rendering."""
|
||||||
|
|
||||||
def __init__(self, exception):
|
def __init__(self, exception: jinja2.TemplateError) -> None:
|
||||||
"""Init the error."""
|
"""Init the error."""
|
||||||
super().__init__('{}: {}'.format(exception.__class__.__name__,
|
super().__init__('{}: {}'.format(exception.__class__.__name__,
|
||||||
exception))
|
exception))
|
||||||
|
@ -93,7 +93,7 @@ def get_component(hass, comp_or_platform) -> Optional[ModuleType]:
|
|||||||
# This prevents that when only
|
# This prevents that when only
|
||||||
# custom_components/switch/some_platform.py exists,
|
# custom_components/switch/some_platform.py exists,
|
||||||
# the import custom_components.switch would succeed.
|
# the import custom_components.switch would succeed.
|
||||||
if module.__spec__.origin == 'namespace':
|
if module.__spec__ and module.__spec__.origin == 'namespace':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
_LOGGER.info("Loaded %s from %s", comp_or_platform, path)
|
_LOGGER.info("Loaded %s from %s", comp_or_platform, path)
|
||||||
|
@ -139,10 +139,11 @@ async def _async_setup_component(hass: core.HomeAssistant,
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if hasattr(component, 'async_setup'):
|
if hasattr(component, 'async_setup'):
|
||||||
result = await component.async_setup(hass, processed_config)
|
result = await component.async_setup( # type: ignore
|
||||||
|
hass, processed_config)
|
||||||
else:
|
else:
|
||||||
result = await hass.async_add_job(
|
result = await hass.async_add_job(
|
||||||
component.setup, hass, processed_config)
|
component.setup, hass, processed_config) # type: ignore
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.exception("Error during setup of component %s", domain)
|
_LOGGER.exception("Error during setup of component %s", domain)
|
||||||
async_notify_setup_error(hass, domain, True)
|
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):
|
for entry in hass.config_entries.async_entries(domain):
|
||||||
await entry.async_setup(hass, component=component)
|
await entry.async_setup(hass, component=component)
|
||||||
|
|
||||||
hass.config.components.add(component.DOMAIN)
|
hass.config.components.add(component.DOMAIN) # type: ignore
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
if domain in hass.data[DATA_SETUP]:
|
if domain in hass.data[DATA_SETUP]:
|
||||||
hass.data[DATA_SETUP].pop(domain)
|
hass.data[DATA_SETUP].pop(domain)
|
||||||
|
|
||||||
hass.bus.async_fire(
|
hass.bus.async_fire(
|
||||||
EVENT_COMPONENT_LOADED, {ATTR_COMPONENT: component.DOMAIN}
|
EVENT_COMPONENT_LOADED,
|
||||||
|
{ATTR_COMPONENT: component.DOMAIN} # type: ignore
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -24,7 +24,7 @@ logging.basicConfig(level=logging.INFO)
|
|||||||
logging.getLogger('sqlalchemy.engine').setLevel(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."""
|
"""Force a function to require a keyword _test_real to be passed in."""
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def guard_func(*args, **kwargs):
|
def guard_func(*args, **kwargs):
|
||||||
@ -40,8 +40,8 @@ def test_real(func):
|
|||||||
|
|
||||||
|
|
||||||
# Guard a few functions that would make network connections
|
# Guard a few functions that would make network connections
|
||||||
location.detect_location_info = test_real(location.detect_location_info)
|
location.detect_location_info = check_real(location.detect_location_info)
|
||||||
location.elevation = test_real(location.elevation)
|
location.elevation = check_real(location.elevation)
|
||||||
util.get_local_ip = lambda: '127.0.0.1'
|
util.get_local_ip = lambda: '127.0.0.1'
|
||||||
|
|
||||||
|
|
||||||
|
3
tox.ini
3
tox.ini
@ -38,7 +38,8 @@ commands =
|
|||||||
|
|
||||||
[testenv:typing]
|
[testenv:typing]
|
||||||
basepython = {env:PYTHON3_PATH:python3}
|
basepython = {env:PYTHON3_PATH:python3}
|
||||||
|
whitelist_externals=/bin/bash
|
||||||
deps =
|
deps =
|
||||||
-r{toxinidir}/requirements_test.txt
|
-r{toxinidir}/requirements_test.txt
|
||||||
commands =
|
commands =
|
||||||
mypy --ignore-missing-imports --follow-imports=skip homeassistant
|
/bin/bash -c 'mypy --ignore-missing-imports --follow-imports=silent homeassistant/*.py'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user