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 checkerpull/2584/head
parent
d570d38d5c
commit
08226a4864
|
@ -8,8 +8,13 @@ matrix:
|
|||
env: TOXENV=requirements
|
||||
- python: "3.5"
|
||||
env: TOXENV=lint
|
||||
- python: "3.5"
|
||||
env: TOXENV=typing
|
||||
- python: "3.5"
|
||||
env: TOXENV=py35
|
||||
allow_failures:
|
||||
- python: "3.5"
|
||||
env: TOXENV=typing
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
|
|
|
@ -8,6 +8,8 @@ import subprocess
|
|||
import sys
|
||||
import threading
|
||||
|
||||
from typing import Optional, List
|
||||
|
||||
from homeassistant.const import (
|
||||
__version__,
|
||||
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."""
|
||||
major, minor = sys.version_info[:2]
|
||||
req_major, req_minor = REQUIRED_PYTHON_VER
|
||||
|
@ -27,7 +29,7 @@ def validate_python():
|
|||
sys.exit(1)
|
||||
|
||||
|
||||
def ensure_config_path(config_dir):
|
||||
def ensure_config_path(config_dir: str) -> None:
|
||||
"""Validate the configuration directory."""
|
||||
import homeassistant.config as config_util
|
||||
lib_dir = os.path.join(config_dir, 'deps')
|
||||
|
@ -56,7 +58,7 @@ def ensure_config_path(config_dir):
|
|||
sys.exit(1)
|
||||
|
||||
|
||||
def ensure_config_file(config_dir):
|
||||
def ensure_config_file(config_dir: str) -> str:
|
||||
"""Ensure configuration file exists."""
|
||||
import homeassistant.config as config_util
|
||||
config_path = config_util.ensure_config_exists(config_dir)
|
||||
|
@ -68,7 +70,7 @@ def ensure_config_file(config_dir):
|
|||
return config_path
|
||||
|
||||
|
||||
def get_arguments():
|
||||
def get_arguments() -> argparse.Namespace:
|
||||
"""Get parsed passed in arguments."""
|
||||
import homeassistant.config as config_util
|
||||
parser = argparse.ArgumentParser(
|
||||
|
@ -125,12 +127,12 @@ def get_arguments():
|
|||
|
||||
arguments = parser.parse_args()
|
||||
if os.name != "posix" or arguments.debug or arguments.runner:
|
||||
arguments.daemon = False
|
||||
setattr(arguments, 'daemon', False)
|
||||
|
||||
return arguments
|
||||
|
||||
|
||||
def daemonize():
|
||||
def daemonize() -> None:
|
||||
"""Move current process to daemon process."""
|
||||
# Create first fork
|
||||
pid = os.fork()
|
||||
|
@ -155,7 +157,7 @@ def daemonize():
|
|||
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 pid file
|
||||
try:
|
||||
|
@ -177,7 +179,7 @@ def check_pid(pid_file):
|
|||
sys.exit(1)
|
||||
|
||||
|
||||
def write_pid(pid_file):
|
||||
def write_pid(pid_file: str) -> None:
|
||||
"""Create a PID File."""
|
||||
pid = os.getpid()
|
||||
try:
|
||||
|
@ -187,7 +189,7 @@ def write_pid(pid_file):
|
|||
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.
|
||||
|
||||
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
|
||||
|
||||
|
||||
def cmdline():
|
||||
def cmdline() -> List[str]:
|
||||
"""Collect path and arguments to re-execute the current hass instance."""
|
||||
if sys.argv[0].endswith('/__main__.py'):
|
||||
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']
|
||||
|
||||
|
||||
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."""
|
||||
from homeassistant import bootstrap
|
||||
|
||||
# Run a simple daemon runner process on Windows to handle restarts
|
||||
if os.name == 'nt' and '--runner' not in sys.argv:
|
||||
args = cmdline() + ['--runner']
|
||||
nt_args = cmdline() + ['--runner']
|
||||
while True:
|
||||
try:
|
||||
subprocess.check_call(args)
|
||||
subprocess.check_call(nt_args)
|
||||
sys.exit(0)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
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)
|
||||
|
||||
if hass is None:
|
||||
return
|
||||
return None
|
||||
|
||||
if args.open_ui:
|
||||
def open_browser(event):
|
||||
|
@ -261,7 +264,7 @@ def setup_and_run_hass(config_dir, args):
|
|||
return exit_code
|
||||
|
||||
|
||||
def try_to_restart():
|
||||
def try_to_restart() -> None:
|
||||
"""Attempt to clean up state and start a new homeassistant instance."""
|
||||
# Things should be mostly shut down already at this point, now just try
|
||||
# to clean up things that may have been left behind.
|
||||
|
@ -303,7 +306,7 @@ def try_to_restart():
|
|||
os.execv(args[0], args)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> int:
|
||||
"""Start Home Assistant."""
|
||||
validate_python()
|
||||
|
||||
|
|
|
@ -6,9 +6,11 @@ import os
|
|||
import sys
|
||||
from collections import defaultdict
|
||||
from threading import RLock
|
||||
from typing import Any, Optional, Dict
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
|
||||
import homeassistant.components as core_components
|
||||
from homeassistant.components import group, persistent_notification
|
||||
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
|
||||
def from_config_dict(config, hass=None, config_dir=None, enable_log=True,
|
||||
verbose=False, skip_pip=False,
|
||||
log_rotate_days=None):
|
||||
def from_config_dict(config: Dict[str, Any],
|
||||
hass: Optional[core.HomeAssistant]=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.
|
||||
|
||||
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
|
||||
|
||||
|
||||
def from_config_file(config_path, hass=None, verbose=False, skip_pip=True,
|
||||
log_rotate_days=None):
|
||||
def from_config_file(config_path: str,
|
||||
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.
|
||||
|
||||
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."""
|
||||
data_dir = os.getenv('APPDATA') if os.name == "nt" \
|
||||
else os.path.expanduser('~')
|
||||
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.
|
||||
|
||||
Creating a default one if needed.
|
||||
|
|
|
@ -57,7 +57,7 @@ class CoreState(enum.Enum):
|
|||
running = "RUNNING"
|
||||
stopping = "STOPPING"
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
"""Return the event."""
|
||||
return self.value
|
||||
|
||||
|
@ -75,11 +75,11 @@ class HomeAssistant(object):
|
|||
self.state = CoreState.not_running
|
||||
|
||||
@property
|
||||
def is_running(self):
|
||||
def is_running(self) -> bool:
|
||||
"""Return if Home Assistant is running."""
|
||||
return self.state == CoreState.running
|
||||
|
||||
def start(self):
|
||||
def start(self) -> None:
|
||||
"""Start home assistant."""
|
||||
_LOGGER.info(
|
||||
"Starting Home Assistant (%d threads)", self.pool.worker_count)
|
||||
|
@ -90,7 +90,7 @@ class HomeAssistant(object):
|
|||
self.pool.block_till_done()
|
||||
self.state = CoreState.running
|
||||
|
||||
def block_till_stopped(self):
|
||||
def block_till_stopped(self) -> int:
|
||||
"""Register service homeassistant/stop and will block until called."""
|
||||
request_shutdown = threading.Event()
|
||||
request_restart = threading.Event()
|
||||
|
@ -132,7 +132,7 @@ class HomeAssistant(object):
|
|||
|
||||
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."""
|
||||
_LOGGER.info("Stopping")
|
||||
self.state = CoreState.stopping
|
||||
|
@ -290,7 +290,7 @@ class EventBus(object):
|
|||
# available to execute this listener it might occur that the
|
||||
# listener gets lined up twice to be executed.
|
||||
# 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)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import importlib
|
|||
import os
|
||||
|
||||
|
||||
def run(args):
|
||||
def run(args: str) -> int:
|
||||
"""Run a script."""
|
||||
scripts = [fil[:-3] for fil in os.listdir(os.path.dirname(__file__))
|
||||
if fil.endswith('.py') and fil != '__init__.py']
|
||||
|
@ -19,4 +19,4 @@ def run(args):
|
|||
return 1
|
||||
|
||||
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
|
||||
jinja2>=2.8
|
||||
voluptuous==0.8.9
|
||||
typing>=3,<4
|
||||
sqlalchemy==1.0.14
|
||||
|
||||
# homeassistant.components.isy994
|
||||
|
|
|
@ -7,3 +7,4 @@ pytest-timeout>=1.0.0
|
|||
pytest-capturelog>=0.7
|
||||
pydocstyle>=1.0.0
|
||||
requests_mock>=1.0
|
||||
mypy-lang>=0.4
|
||||
|
|
1
setup.py
1
setup.py
|
@ -17,6 +17,7 @@ REQUIRES = [
|
|||
'pip>=7.0.0',
|
||||
'jinja2>=2.8',
|
||||
'voluptuous==0.8.9',
|
||||
'typing>=3,<4',
|
||||
'sqlalchemy==1.0.14',
|
||||
]
|
||||
|
||||
|
|
7
tox.ini
7
tox.ini
|
@ -1,5 +1,5 @@
|
|||
[tox]
|
||||
envlist = py34, py35, lint, requirements
|
||||
envlist = py34, py35, lint, requirements, typing
|
||||
skip_missing_interpreters = True
|
||||
|
||||
[testenv]
|
||||
|
@ -29,3 +29,8 @@ basepython = python3
|
|||
deps =
|
||||
commands =
|
||||
python script/gen_requirements_all.py validate
|
||||
|
||||
[testenv:typing]
|
||||
basepython = python3
|
||||
commands =
|
||||
mypy --silent-imports homeassistant
|
||||
|
|
Loading…
Reference in New Issue