Type Hints - __main__ (#2574)
* Add __main__ type hints * Fix most errors of __main__ * Add ignore for script.run() * Add type annotations for from_config_dict and from_config_file * Fix errors * Fix requirement error * Add mypy type check to tests * Enable travis typing check * Messed up the tox deps * Laxer type checker
This commit is contained in:
parent
d570d38d5c
commit
08226a4864
10 changed files with 58 additions and 32 deletions
|
@ -8,8 +8,13 @@ matrix:
|
||||||
env: TOXENV=requirements
|
env: TOXENV=requirements
|
||||||
- python: "3.5"
|
- python: "3.5"
|
||||||
env: TOXENV=lint
|
env: TOXENV=lint
|
||||||
|
- python: "3.5"
|
||||||
|
env: TOXENV=typing
|
||||||
- python: "3.5"
|
- python: "3.5"
|
||||||
env: TOXENV=py35
|
env: TOXENV=py35
|
||||||
|
allow_failures:
|
||||||
|
- python: "3.5"
|
||||||
|
env: TOXENV=typing
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.cache/pip
|
- $HOME/.cache/pip
|
||||||
|
|
|
@ -8,6 +8,8 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
__version__,
|
__version__,
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
|
@ -16,7 +18,7 @@ from homeassistant.const import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_python():
|
def validate_python() -> None:
|
||||||
"""Validate we're running the right Python version."""
|
"""Validate we're running the right Python version."""
|
||||||
major, minor = sys.version_info[:2]
|
major, minor = sys.version_info[:2]
|
||||||
req_major, req_minor = REQUIRED_PYTHON_VER
|
req_major, req_minor = REQUIRED_PYTHON_VER
|
||||||
|
@ -27,7 +29,7 @@ def validate_python():
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def ensure_config_path(config_dir):
|
def ensure_config_path(config_dir: str) -> None:
|
||||||
"""Validate the configuration directory."""
|
"""Validate the configuration directory."""
|
||||||
import homeassistant.config as config_util
|
import homeassistant.config as config_util
|
||||||
lib_dir = os.path.join(config_dir, 'deps')
|
lib_dir = os.path.join(config_dir, 'deps')
|
||||||
|
@ -56,7 +58,7 @@ def ensure_config_path(config_dir):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def ensure_config_file(config_dir):
|
def ensure_config_file(config_dir: str) -> str:
|
||||||
"""Ensure configuration file exists."""
|
"""Ensure configuration file exists."""
|
||||||
import homeassistant.config as config_util
|
import homeassistant.config as config_util
|
||||||
config_path = config_util.ensure_config_exists(config_dir)
|
config_path = config_util.ensure_config_exists(config_dir)
|
||||||
|
@ -68,7 +70,7 @@ def ensure_config_file(config_dir):
|
||||||
return config_path
|
return config_path
|
||||||
|
|
||||||
|
|
||||||
def get_arguments():
|
def get_arguments() -> argparse.Namespace:
|
||||||
"""Get parsed passed in arguments."""
|
"""Get parsed passed in arguments."""
|
||||||
import homeassistant.config as config_util
|
import homeassistant.config as config_util
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
|
@ -125,12 +127,12 @@ def get_arguments():
|
||||||
|
|
||||||
arguments = parser.parse_args()
|
arguments = parser.parse_args()
|
||||||
if os.name != "posix" or arguments.debug or arguments.runner:
|
if os.name != "posix" or arguments.debug or arguments.runner:
|
||||||
arguments.daemon = False
|
setattr(arguments, 'daemon', False)
|
||||||
|
|
||||||
return arguments
|
return arguments
|
||||||
|
|
||||||
|
|
||||||
def daemonize():
|
def daemonize() -> None:
|
||||||
"""Move current process to daemon process."""
|
"""Move current process to daemon process."""
|
||||||
# Create first fork
|
# Create first fork
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
|
@ -155,7 +157,7 @@ def daemonize():
|
||||||
os.dup2(outfd.fileno(), sys.stderr.fileno())
|
os.dup2(outfd.fileno(), sys.stderr.fileno())
|
||||||
|
|
||||||
|
|
||||||
def check_pid(pid_file):
|
def check_pid(pid_file: str) -> None:
|
||||||
"""Check that HA is not already running."""
|
"""Check that HA is not already running."""
|
||||||
# Check pid file
|
# Check pid file
|
||||||
try:
|
try:
|
||||||
|
@ -177,7 +179,7 @@ def check_pid(pid_file):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def write_pid(pid_file):
|
def write_pid(pid_file: str) -> None:
|
||||||
"""Create a PID File."""
|
"""Create a PID File."""
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
try:
|
try:
|
||||||
|
@ -187,7 +189,7 @@ def write_pid(pid_file):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def closefds_osx(min_fd, max_fd):
|
def closefds_osx(min_fd: int, max_fd: int) -> None:
|
||||||
"""Make sure file descriptors get closed when we restart.
|
"""Make sure file descriptors get closed when we restart.
|
||||||
|
|
||||||
We cannot call close on guarded fds, and we cannot easily test which fds
|
We cannot call close on guarded fds, and we cannot easily test which fds
|
||||||
|
@ -205,7 +207,7 @@ def closefds_osx(min_fd, max_fd):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def cmdline():
|
def cmdline() -> List[str]:
|
||||||
"""Collect path and arguments to re-execute the current hass instance."""
|
"""Collect path and arguments to re-execute the current hass instance."""
|
||||||
if sys.argv[0].endswith('/__main__.py'):
|
if sys.argv[0].endswith('/__main__.py'):
|
||||||
modulepath = os.path.dirname(sys.argv[0])
|
modulepath = os.path.dirname(sys.argv[0])
|
||||||
|
@ -213,16 +215,17 @@ def cmdline():
|
||||||
return [sys.executable] + [arg for arg in sys.argv if arg != '--daemon']
|
return [sys.executable] + [arg for arg in sys.argv if arg != '--daemon']
|
||||||
|
|
||||||
|
|
||||||
def setup_and_run_hass(config_dir, args):
|
def setup_and_run_hass(config_dir: str,
|
||||||
|
args: argparse.Namespace) -> Optional[int]:
|
||||||
"""Setup HASS and run."""
|
"""Setup HASS and run."""
|
||||||
from homeassistant import bootstrap
|
from homeassistant import bootstrap
|
||||||
|
|
||||||
# Run a simple daemon runner process on Windows to handle restarts
|
# Run a simple daemon runner process on Windows to handle restarts
|
||||||
if os.name == 'nt' and '--runner' not in sys.argv:
|
if os.name == 'nt' and '--runner' not in sys.argv:
|
||||||
args = cmdline() + ['--runner']
|
nt_args = cmdline() + ['--runner']
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(args)
|
subprocess.check_call(nt_args)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
except subprocess.CalledProcessError as exc:
|
except subprocess.CalledProcessError as exc:
|
||||||
if exc.returncode != RESTART_EXIT_CODE:
|
if exc.returncode != RESTART_EXIT_CODE:
|
||||||
|
@ -244,7 +247,7 @@ def setup_and_run_hass(config_dir, args):
|
||||||
log_rotate_days=args.log_rotate_days)
|
log_rotate_days=args.log_rotate_days)
|
||||||
|
|
||||||
if hass is None:
|
if hass is None:
|
||||||
return
|
return None
|
||||||
|
|
||||||
if args.open_ui:
|
if args.open_ui:
|
||||||
def open_browser(event):
|
def open_browser(event):
|
||||||
|
@ -261,7 +264,7 @@ def setup_and_run_hass(config_dir, args):
|
||||||
return exit_code
|
return exit_code
|
||||||
|
|
||||||
|
|
||||||
def try_to_restart():
|
def try_to_restart() -> None:
|
||||||
"""Attempt to clean up state and start a new homeassistant instance."""
|
"""Attempt to clean up state and start a new homeassistant instance."""
|
||||||
# Things should be mostly shut down already at this point, now just try
|
# Things should be mostly shut down already at this point, now just try
|
||||||
# to clean up things that may have been left behind.
|
# to clean up things that may have been left behind.
|
||||||
|
@ -303,7 +306,7 @@ def try_to_restart():
|
||||||
os.execv(args[0], args)
|
os.execv(args[0], args)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> int:
|
||||||
"""Start Home Assistant."""
|
"""Start Home Assistant."""
|
||||||
validate_python()
|
validate_python()
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,11 @@ import os
|
||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
|
from typing import Any, Optional, Dict
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
|
||||||
import homeassistant.components as core_components
|
import homeassistant.components as core_components
|
||||||
from homeassistant.components import group, persistent_notification
|
from homeassistant.components import group, persistent_notification
|
||||||
import homeassistant.config as conf_util
|
import homeassistant.config as conf_util
|
||||||
|
@ -202,9 +204,14 @@ def prepare_setup_platform(hass, config, domain, platform_name):
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-branches, too-many-statements, too-many-arguments
|
# pylint: disable=too-many-branches, too-many-statements, too-many-arguments
|
||||||
def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
def from_config_dict(config: Dict[str, Any],
|
||||||
verbose=False, skip_pip=False,
|
hass: Optional[core.HomeAssistant]=None,
|
||||||
log_rotate_days=None):
|
config_dir: Optional[str]=None,
|
||||||
|
enable_log: bool=True,
|
||||||
|
verbose: bool=False,
|
||||||
|
skip_pip: bool=False,
|
||||||
|
log_rotate_days: Any=None) \
|
||||||
|
-> Optional[core.HomeAssistant]:
|
||||||
"""Try to configure Home Assistant from a config dict.
|
"""Try to configure Home Assistant from a config dict.
|
||||||
|
|
||||||
Dynamically loads required components and its dependencies.
|
Dynamically loads required components and its dependencies.
|
||||||
|
@ -266,8 +273,11 @@ def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
||||||
return hass
|
return hass
|
||||||
|
|
||||||
|
|
||||||
def from_config_file(config_path, hass=None, verbose=False, skip_pip=True,
|
def from_config_file(config_path: str,
|
||||||
log_rotate_days=None):
|
hass: Optional[core.HomeAssistant]=None,
|
||||||
|
verbose: bool=False,
|
||||||
|
skip_pip: bool=True,
|
||||||
|
log_rotate_days: Any=None):
|
||||||
"""Read the configuration file and try to start all the functionality.
|
"""Read the configuration file and try to start all the functionality.
|
||||||
|
|
||||||
Will add functionality to 'hass' parameter if given,
|
Will add functionality to 'hass' parameter if given,
|
||||||
|
|
|
@ -73,14 +73,14 @@ CORE_CONFIG_SCHEMA = vol.Schema({
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def get_default_config_dir():
|
def get_default_config_dir() -> str:
|
||||||
"""Put together the default configuration directory based on OS."""
|
"""Put together the default configuration directory based on 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)
|
||||||
|
|
||||||
|
|
||||||
def ensure_config_exists(config_dir, detect_location=True):
|
def ensure_config_exists(config_dir: str, detect_location: bool=True) -> str:
|
||||||
"""Ensure a config file exists in given configuration directory.
|
"""Ensure a config file exists in given configuration directory.
|
||||||
|
|
||||||
Creating a default one if needed.
|
Creating a default one if needed.
|
||||||
|
|
|
@ -57,7 +57,7 @@ class CoreState(enum.Enum):
|
||||||
running = "RUNNING"
|
running = "RUNNING"
|
||||||
stopping = "STOPPING"
|
stopping = "STOPPING"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
"""Return the event."""
|
"""Return the event."""
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
|
@ -75,11 +75,11 @@ class HomeAssistant(object):
|
||||||
self.state = CoreState.not_running
|
self.state = CoreState.not_running
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_running(self):
|
def is_running(self) -> bool:
|
||||||
"""Return if Home Assistant is running."""
|
"""Return if Home Assistant is running."""
|
||||||
return self.state == CoreState.running
|
return self.state == CoreState.running
|
||||||
|
|
||||||
def start(self):
|
def start(self) -> None:
|
||||||
"""Start home assistant."""
|
"""Start home assistant."""
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Starting Home Assistant (%d threads)", self.pool.worker_count)
|
"Starting Home Assistant (%d threads)", self.pool.worker_count)
|
||||||
|
@ -90,7 +90,7 @@ class HomeAssistant(object):
|
||||||
self.pool.block_till_done()
|
self.pool.block_till_done()
|
||||||
self.state = CoreState.running
|
self.state = CoreState.running
|
||||||
|
|
||||||
def block_till_stopped(self):
|
def block_till_stopped(self) -> int:
|
||||||
"""Register service homeassistant/stop and will block until called."""
|
"""Register service homeassistant/stop and will block until called."""
|
||||||
request_shutdown = threading.Event()
|
request_shutdown = threading.Event()
|
||||||
request_restart = threading.Event()
|
request_restart = threading.Event()
|
||||||
|
@ -132,7 +132,7 @@ class HomeAssistant(object):
|
||||||
|
|
||||||
return RESTART_EXIT_CODE if request_restart.isSet() else 0
|
return RESTART_EXIT_CODE if request_restart.isSet() else 0
|
||||||
|
|
||||||
def stop(self):
|
def stop(self) -> None:
|
||||||
"""Stop Home Assistant and shuts down all threads."""
|
"""Stop Home Assistant and shuts down all threads."""
|
||||||
_LOGGER.info("Stopping")
|
_LOGGER.info("Stopping")
|
||||||
self.state = CoreState.stopping
|
self.state = CoreState.stopping
|
||||||
|
@ -290,7 +290,7 @@ class EventBus(object):
|
||||||
# available to execute this listener it might occur that the
|
# available to execute this listener it might occur that the
|
||||||
# listener gets lined up twice to be executed.
|
# listener gets lined up twice to be executed.
|
||||||
# This will make sure the second time it does nothing.
|
# This will make sure the second time it does nothing.
|
||||||
onetime_listener.run = True
|
setattr(onetime_listener, 'run', True)
|
||||||
|
|
||||||
self.remove_listener(event_type, onetime_listener)
|
self.remove_listener(event_type, onetime_listener)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import importlib
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
def run(args):
|
def run(args: str) -> int:
|
||||||
"""Run a script."""
|
"""Run a script."""
|
||||||
scripts = [fil[:-3] for fil in os.listdir(os.path.dirname(__file__))
|
scripts = [fil[:-3] for fil in os.listdir(os.path.dirname(__file__))
|
||||||
if fil.endswith('.py') and fil != '__init__.py']
|
if fil.endswith('.py') and fil != '__init__.py']
|
||||||
|
@ -19,4 +19,4 @@ def run(args):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
script = importlib.import_module('homeassistant.scripts.' + args[0])
|
script = importlib.import_module('homeassistant.scripts.' + args[0])
|
||||||
return script.run(args[1:])
|
return script.run(args[1:]) # type: ignore
|
||||||
|
|
|
@ -5,6 +5,7 @@ pytz>=2016.6.1
|
||||||
pip>=7.0.0
|
pip>=7.0.0
|
||||||
jinja2>=2.8
|
jinja2>=2.8
|
||||||
voluptuous==0.8.9
|
voluptuous==0.8.9
|
||||||
|
typing>=3,<4
|
||||||
sqlalchemy==1.0.14
|
sqlalchemy==1.0.14
|
||||||
|
|
||||||
# homeassistant.components.isy994
|
# homeassistant.components.isy994
|
||||||
|
|
|
@ -7,3 +7,4 @@ pytest-timeout>=1.0.0
|
||||||
pytest-capturelog>=0.7
|
pytest-capturelog>=0.7
|
||||||
pydocstyle>=1.0.0
|
pydocstyle>=1.0.0
|
||||||
requests_mock>=1.0
|
requests_mock>=1.0
|
||||||
|
mypy-lang>=0.4
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -17,6 +17,7 @@ REQUIRES = [
|
||||||
'pip>=7.0.0',
|
'pip>=7.0.0',
|
||||||
'jinja2>=2.8',
|
'jinja2>=2.8',
|
||||||
'voluptuous==0.8.9',
|
'voluptuous==0.8.9',
|
||||||
|
'typing>=3,<4',
|
||||||
'sqlalchemy==1.0.14',
|
'sqlalchemy==1.0.14',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
7
tox.ini
7
tox.ini
|
@ -1,5 +1,5 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py34, py35, lint, requirements
|
envlist = py34, py35, lint, requirements, typing
|
||||||
skip_missing_interpreters = True
|
skip_missing_interpreters = True
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
|
@ -29,3 +29,8 @@ basepython = python3
|
||||||
deps =
|
deps =
|
||||||
commands =
|
commands =
|
||||||
python script/gen_requirements_all.py validate
|
python script/gen_requirements_all.py validate
|
||||||
|
|
||||||
|
[testenv:typing]
|
||||||
|
basepython = python3
|
||||||
|
commands =
|
||||||
|
mypy --silent-imports homeassistant
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue