Skip to content

Commit

Permalink
Type Hints - __main__ (home-assistant#2574)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
fabianhjr authored and balloob committed Jul 21, 2016
1 parent d570d38 commit 08226a4
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 19 additions & 16 deletions homeassistant/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import sys
import threading

from typing import Optional, List

from homeassistant.const import (
__version__,
EVENT_HOMEASSISTANT_START,
Expand All @@ -16,7 +18,7 @@
)


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
Expand All @@ -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')
Expand Down Expand Up @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -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()
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -205,24 +207,25 @@ 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])
os.environ['PYTHONPATH'] = os.path.dirname(modulepath)
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:
Expand All @@ -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):
Expand All @@ -261,7 +264,7 @@ def open_browser(event):
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.
Expand Down Expand Up @@ -303,7 +306,7 @@ def try_to_restart():
os.execv(args[0], args)


def main():
def main() -> int:
"""Start Home Assistant."""
validate_python()

Expand Down
20 changes: 15 additions & 5 deletions homeassistant/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ def _valid_customize(value):
})


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.
Expand Down
12 changes: 6 additions & 6 deletions homeassistant/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -75,11 +75,11 @@ def __init__(self):
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)
Expand All @@ -90,7 +90,7 @@ def start(self):
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()
Expand Down Expand Up @@ -132,7 +132,7 @@ def restart_homeassistant(*args):

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
Expand Down Expand Up @@ -290,7 +290,7 @@ def onetime_listener(event):
# 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)

Expand Down
4 changes: 2 additions & 2 deletions homeassistant/scripts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
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']
Expand All @@ -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
1 change: 1 addition & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions requirements_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
'pip>=7.0.0',
'jinja2>=2.8',
'voluptuous==0.8.9',
'typing>=3,<4',
'sqlalchemy==1.0.14',
]

Expand Down
7 changes: 6 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py34, py35, lint, requirements
envlist = py34, py35, lint, requirements, typing
skip_missing_interpreters = True

[testenv]
Expand Down Expand Up @@ -29,3 +29,8 @@ basepython = python3
deps =
commands =
python script/gen_requirements_all.py validate

[testenv:typing]
basepython = python3
commands =
mypy --silent-imports homeassistant

0 comments on commit 08226a4

Please sign in to comment.