Hello Python 3.5 (#12610)
* Hello Python 3.5 * Fix test * Fix tests * Fix never awaited block till done warnings
This commit is contained in:
parent
156206dfee
commit
6ee3c1b3e5
10 changed files with 36 additions and 56 deletions
|
@ -6,12 +6,10 @@ addons:
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
include:
|
include:
|
||||||
- python: "3.4.2"
|
- python: "3.5.3"
|
||||||
env: TOXENV=lint
|
env: TOXENV=lint
|
||||||
- python: "3.4.2"
|
- python: "3.5.3"
|
||||||
env: TOXENV=pylint
|
env: TOXENV=pylint
|
||||||
- python: "3.4.2"
|
|
||||||
env: TOXENV=py34
|
|
||||||
# - python: "3.5"
|
# - python: "3.5"
|
||||||
# env: TOXENV=typing
|
# env: TOXENV=typing
|
||||||
- python: "3.5.3"
|
- python: "3.5.3"
|
||||||
|
|
|
@ -15,7 +15,6 @@ from homeassistant.const import (
|
||||||
__version__,
|
__version__,
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
REQUIRED_PYTHON_VER,
|
REQUIRED_PYTHON_VER,
|
||||||
REQUIRED_PYTHON_VER_WIN,
|
|
||||||
RESTART_EXIT_CODE,
|
RESTART_EXIT_CODE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,12 +32,7 @@ def attempt_use_uvloop():
|
||||||
|
|
||||||
def validate_python() -> None:
|
def validate_python() -> None:
|
||||||
"""Validate that the right Python version is running."""
|
"""Validate that the right Python version is running."""
|
||||||
if sys.platform == "win32" and \
|
if sys.version_info[:3] < REQUIRED_PYTHON_VER:
|
||||||
sys.version_info[:3] < REQUIRED_PYTHON_VER_WIN:
|
|
||||||
print("Home Assistant requires at least Python {}.{}.{}".format(
|
|
||||||
*REQUIRED_PYTHON_VER_WIN))
|
|
||||||
sys.exit(1)
|
|
||||||
elif sys.version_info[:3] < REQUIRED_PYTHON_VER:
|
|
||||||
print("Home Assistant requires at least Python {}.{}.{}".format(
|
print("Home Assistant requires at least Python {}.{}.{}".format(
|
||||||
*REQUIRED_PYTHON_VER))
|
*REQUIRED_PYTHON_VER))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
|
@ -5,8 +5,7 @@ MINOR_VERSION = 65
|
||||||
PATCH_VERSION = '0.dev0'
|
PATCH_VERSION = '0.dev0'
|
||||||
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
|
||||||
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)
|
||||||
REQUIRED_PYTHON_VER = (3, 4, 2)
|
REQUIRED_PYTHON_VER = (3, 5, 3)
|
||||||
REQUIRED_PYTHON_VER_WIN = (3, 5, 2)
|
|
||||||
|
|
||||||
# Format for platforms
|
# Format for platforms
|
||||||
PLATFORM_FORMAT = '{}.{}'
|
PLATFORM_FORMAT = '{}.{}'
|
||||||
|
|
|
@ -164,8 +164,7 @@ class HomeAssistant(object):
|
||||||
finally:
|
finally:
|
||||||
self.loop.close()
|
self.loop.close()
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def async_start(self):
|
||||||
def async_start(self):
|
|
||||||
"""Finalize startup from inside the event loop.
|
"""Finalize startup from inside the event loop.
|
||||||
|
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
|
@ -181,7 +180,7 @@ class HomeAssistant(object):
|
||||||
# Only block for EVENT_HOMEASSISTANT_START listener
|
# Only block for EVENT_HOMEASSISTANT_START listener
|
||||||
self.async_stop_track_tasks()
|
self.async_stop_track_tasks()
|
||||||
with timeout(TIMEOUT_EVENT_START, loop=self.loop):
|
with timeout(TIMEOUT_EVENT_START, loop=self.loop):
|
||||||
yield from self.async_block_till_done()
|
await self.async_block_till_done()
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
'Something is blocking Home Assistant from wrapping up the '
|
'Something is blocking Home Assistant from wrapping up the '
|
||||||
|
@ -190,7 +189,7 @@ class HomeAssistant(object):
|
||||||
', '.join(self.config.components))
|
', '.join(self.config.components))
|
||||||
|
|
||||||
# Allow automations to set up the start triggers before changing state
|
# Allow automations to set up the start triggers before changing state
|
||||||
yield from asyncio.sleep(0, loop=self.loop)
|
await asyncio.sleep(0, loop=self.loop)
|
||||||
self.state = CoreState.running
|
self.state = CoreState.running
|
||||||
_async_create_timer(self)
|
_async_create_timer(self)
|
||||||
|
|
||||||
|
@ -259,27 +258,25 @@ class HomeAssistant(object):
|
||||||
run_coroutine_threadsafe(
|
run_coroutine_threadsafe(
|
||||||
self.async_block_till_done(), loop=self.loop).result()
|
self.async_block_till_done(), loop=self.loop).result()
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def async_block_till_done(self):
|
||||||
def async_block_till_done(self):
|
|
||||||
"""Block till all pending work is done."""
|
"""Block till all pending work is done."""
|
||||||
# To flush out any call_soon_threadsafe
|
# To flush out any call_soon_threadsafe
|
||||||
yield from asyncio.sleep(0, loop=self.loop)
|
await asyncio.sleep(0, loop=self.loop)
|
||||||
|
|
||||||
while self._pending_tasks:
|
while self._pending_tasks:
|
||||||
pending = [task for task in self._pending_tasks
|
pending = [task for task in self._pending_tasks
|
||||||
if not task.done()]
|
if not task.done()]
|
||||||
self._pending_tasks.clear()
|
self._pending_tasks.clear()
|
||||||
if pending:
|
if pending:
|
||||||
yield from asyncio.wait(pending, loop=self.loop)
|
await asyncio.wait(pending, loop=self.loop)
|
||||||
else:
|
else:
|
||||||
yield from asyncio.sleep(0, loop=self.loop)
|
await asyncio.sleep(0, loop=self.loop)
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
"""Stop Home Assistant and shuts down all threads."""
|
"""Stop Home Assistant and shuts down all threads."""
|
||||||
fire_coroutine_threadsafe(self.async_stop(), self.loop)
|
fire_coroutine_threadsafe(self.async_stop(), self.loop)
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def async_stop(self, exit_code=0) -> None:
|
||||||
def async_stop(self, exit_code=0) -> None:
|
|
||||||
"""Stop Home Assistant and shuts down all threads.
|
"""Stop Home Assistant and shuts down all threads.
|
||||||
|
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
|
@ -288,12 +285,12 @@ class HomeAssistant(object):
|
||||||
self.state = CoreState.stopping
|
self.state = CoreState.stopping
|
||||||
self.async_track_tasks()
|
self.async_track_tasks()
|
||||||
self.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
self.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||||
yield from self.async_block_till_done()
|
await self.async_block_till_done()
|
||||||
|
|
||||||
# stage 2
|
# stage 2
|
||||||
self.state = CoreState.not_running
|
self.state = CoreState.not_running
|
||||||
self.bus.async_fire(EVENT_HOMEASSISTANT_CLOSE)
|
self.bus.async_fire(EVENT_HOMEASSISTANT_CLOSE)
|
||||||
yield from self.async_block_till_done()
|
await self.async_block_till_done()
|
||||||
self.executor.shutdown()
|
self.executor.shutdown()
|
||||||
|
|
||||||
self.exit_code = exit_code
|
self.exit_code = exit_code
|
||||||
|
@ -912,8 +909,8 @@ class ServiceRegistry(object):
|
||||||
self._hass.loop
|
self._hass.loop
|
||||||
).result()
|
).result()
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def async_call(self, domain, service, service_data=None,
|
||||||
def async_call(self, domain, service, service_data=None, blocking=False):
|
blocking=False):
|
||||||
"""
|
"""
|
||||||
Call a service.
|
Call a service.
|
||||||
|
|
||||||
|
@ -956,14 +953,13 @@ class ServiceRegistry(object):
|
||||||
self._hass.bus.async_fire(EVENT_CALL_SERVICE, event_data)
|
self._hass.bus.async_fire(EVENT_CALL_SERVICE, event_data)
|
||||||
|
|
||||||
if blocking:
|
if blocking:
|
||||||
done, _ = yield from asyncio.wait(
|
done, _ = await asyncio.wait(
|
||||||
[fut], loop=self._hass.loop, timeout=SERVICE_CALL_LIMIT)
|
[fut], loop=self._hass.loop, timeout=SERVICE_CALL_LIMIT)
|
||||||
success = bool(done)
|
success = bool(done)
|
||||||
unsub()
|
unsub()
|
||||||
return success
|
return success
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def _event_to_service_call(self, event):
|
||||||
def _event_to_service_call(self, event):
|
|
||||||
"""Handle the SERVICE_CALLED events from the EventBus."""
|
"""Handle the SERVICE_CALLED events from the EventBus."""
|
||||||
service_data = event.data.get(ATTR_SERVICE_DATA) or {}
|
service_data = event.data.get(ATTR_SERVICE_DATA) or {}
|
||||||
domain = event.data.get(ATTR_DOMAIN).lower()
|
domain = event.data.get(ATTR_DOMAIN).lower()
|
||||||
|
@ -1007,7 +1003,7 @@ class ServiceRegistry(object):
|
||||||
service_handler.func(service_call)
|
service_handler.func(service_call)
|
||||||
fire_service_executed()
|
fire_service_executed()
|
||||||
elif service_handler.is_coroutinefunction:
|
elif service_handler.is_coroutinefunction:
|
||||||
yield from service_handler.func(service_call)
|
await service_handler.func(service_call)
|
||||||
fire_service_executed()
|
fire_service_executed()
|
||||||
else:
|
else:
|
||||||
def execute_service():
|
def execute_service():
|
||||||
|
@ -1015,7 +1011,7 @@ class ServiceRegistry(object):
|
||||||
service_handler.func(service_call)
|
service_handler.func(service_call)
|
||||||
fire_service_executed()
|
fire_service_executed()
|
||||||
|
|
||||||
yield from self._hass.async_add_job(execute_service)
|
await self._hass.async_add_job(execute_service)
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.exception('Error executing service %s', service_call)
|
_LOGGER.exception('Error executing service %s', service_call)
|
||||||
|
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -1,7 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Home Assistant setup script."""
|
"""Home Assistant setup script."""
|
||||||
import sys
|
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
import homeassistant.const as hass_const
|
import homeassistant.const as hass_const
|
||||||
|
@ -27,7 +25,6 @@ PROJECT_CLASSIFIERS = [
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'License :: OSI Approved :: Apache Software License',
|
'License :: OSI Approved :: Apache Software License',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python :: 3.4',
|
|
||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Topic :: Home Automation'
|
'Topic :: Home Automation'
|
||||||
|
@ -64,9 +61,7 @@ REQUIRES = [
|
||||||
|
|
||||||
MIN_PY_VERSION = '.'.join(map(
|
MIN_PY_VERSION = '.'.join(map(
|
||||||
str,
|
str,
|
||||||
hass_const.REQUIRED_PYTHON_VER_WIN
|
hass_const.REQUIRED_PYTHON_VER))
|
||||||
if sys.platform.startswith('win')
|
|
||||||
else hass_const.REQUIRED_PYTHON_VER))
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name=PROJECT_PACKAGE_NAME,
|
name=PROJECT_PACKAGE_NAME,
|
||||||
|
|
|
@ -114,8 +114,6 @@ def test_valid_credentials(
|
||||||
result = hass.loop.run_until_complete(
|
result = hass.loop.run_until_complete(
|
||||||
async_setup_scanner(hass, config, mock_see))
|
async_setup_scanner(hass, config, mock_see))
|
||||||
|
|
||||||
hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert result
|
assert result
|
||||||
|
|
||||||
assert mock_create_session.called
|
assert mock_create_session.called
|
||||||
|
|
|
@ -420,11 +420,11 @@ def test_proper_put_state_request(hue_client):
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
def perform_put_test_on_ceiling_lights(hass_hue, hue_client,
|
async def perform_put_test_on_ceiling_lights(hass_hue, hue_client,
|
||||||
content_type='application/json'):
|
content_type='application/json'):
|
||||||
"""Test the setting of a light."""
|
"""Test the setting of a light."""
|
||||||
# Turn the office light off first
|
# Turn the office light off first
|
||||||
yield from hass_hue.services.async_call(
|
await hass_hue.services.async_call(
|
||||||
light.DOMAIN, const.SERVICE_TURN_OFF,
|
light.DOMAIN, const.SERVICE_TURN_OFF,
|
||||||
{const.ATTR_ENTITY_ID: 'light.ceiling_lights'},
|
{const.ATTR_ENTITY_ID: 'light.ceiling_lights'},
|
||||||
blocking=True)
|
blocking=True)
|
||||||
|
@ -433,14 +433,14 @@ def perform_put_test_on_ceiling_lights(hass_hue, hue_client,
|
||||||
assert ceiling_lights.state == STATE_OFF
|
assert ceiling_lights.state == STATE_OFF
|
||||||
|
|
||||||
# Go through the API to turn it on
|
# Go through the API to turn it on
|
||||||
office_result = yield from perform_put_light_state(
|
office_result = await perform_put_light_state(
|
||||||
hass_hue, hue_client,
|
hass_hue, hue_client,
|
||||||
'light.ceiling_lights', True, 56, content_type)
|
'light.ceiling_lights', True, 56, content_type)
|
||||||
|
|
||||||
assert office_result.status == 200
|
assert office_result.status == 200
|
||||||
assert 'application/json' in office_result.headers['content-type']
|
assert 'application/json' in office_result.headers['content-type']
|
||||||
|
|
||||||
office_result_json = yield from office_result.json()
|
office_result_json = await office_result.json()
|
||||||
|
|
||||||
assert len(office_result_json) == 2
|
assert len(office_result_json) == 2
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ def test_subscribing_config_topic(hass, mqtt_mock):
|
||||||
assert call_args[2] == 0
|
assert call_args[2] == 0
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
@patch('homeassistant.components.mqtt.discovery.async_load_platform')
|
@patch('homeassistant.components.mqtt.discovery.async_load_platform')
|
||||||
|
@asyncio.coroutine
|
||||||
def test_invalid_topic(mock_load_platform, hass, mqtt_mock):
|
def test_invalid_topic(mock_load_platform, hass, mqtt_mock):
|
||||||
"""Test sending to invalid topic."""
|
"""Test sending to invalid topic."""
|
||||||
mock_load_platform.return_value = mock_coro()
|
mock_load_platform.return_value = mock_coro()
|
||||||
|
@ -34,8 +34,8 @@ def test_invalid_topic(mock_load_platform, hass, mqtt_mock):
|
||||||
assert not mock_load_platform.called
|
assert not mock_load_platform.called
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
@patch('homeassistant.components.mqtt.discovery.async_load_platform')
|
@patch('homeassistant.components.mqtt.discovery.async_load_platform')
|
||||||
|
@asyncio.coroutine
|
||||||
def test_invalid_json(mock_load_platform, hass, mqtt_mock, caplog):
|
def test_invalid_json(mock_load_platform, hass, mqtt_mock, caplog):
|
||||||
"""Test sending in invalid JSON."""
|
"""Test sending in invalid JSON."""
|
||||||
mock_load_platform.return_value = mock_coro()
|
mock_load_platform.return_value = mock_coro()
|
||||||
|
@ -48,8 +48,8 @@ def test_invalid_json(mock_load_platform, hass, mqtt_mock, caplog):
|
||||||
assert not mock_load_platform.called
|
assert not mock_load_platform.called
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
@patch('homeassistant.components.mqtt.discovery.async_load_platform')
|
@patch('homeassistant.components.mqtt.discovery.async_load_platform')
|
||||||
|
@asyncio.coroutine
|
||||||
def test_only_valid_components(mock_load_platform, hass, mqtt_mock, caplog):
|
def test_only_valid_components(mock_load_platform, hass, mqtt_mock, caplog):
|
||||||
"""Test for a valid component."""
|
"""Test for a valid component."""
|
||||||
mock_load_platform.return_value = mock_coro()
|
mock_load_platform.return_value = mock_coro()
|
||||||
|
|
|
@ -165,7 +165,7 @@ class TestRecorderPurge(unittest.TestCase):
|
||||||
|
|
||||||
# run purge method - no service data, use defaults
|
# run purge method - no service data, use defaults
|
||||||
self.hass.services.call('recorder', 'purge')
|
self.hass.services.call('recorder', 'purge')
|
||||||
self.hass.async_block_till_done()
|
self.hass.block_till_done()
|
||||||
|
|
||||||
# Small wait for recorder thread
|
# Small wait for recorder thread
|
||||||
self.hass.data[DATA_INSTANCE].block_till_done()
|
self.hass.data[DATA_INSTANCE].block_till_done()
|
||||||
|
@ -177,7 +177,7 @@ class TestRecorderPurge(unittest.TestCase):
|
||||||
# run purge method - correct service data
|
# run purge method - correct service data
|
||||||
self.hass.services.call('recorder', 'purge',
|
self.hass.services.call('recorder', 'purge',
|
||||||
service_data=service_data)
|
service_data=service_data)
|
||||||
self.hass.async_block_till_done()
|
self.hass.block_till_done()
|
||||||
|
|
||||||
# Small wait for recorder thread
|
# Small wait for recorder thread
|
||||||
self.hass.data[DATA_INSTANCE].block_till_done()
|
self.hass.data[DATA_INSTANCE].block_till_done()
|
||||||
|
@ -203,6 +203,6 @@ class TestRecorderPurge(unittest.TestCase):
|
||||||
self.assertFalse(self.hass.data[DATA_INSTANCE].did_vacuum)
|
self.assertFalse(self.hass.data[DATA_INSTANCE].did_vacuum)
|
||||||
self.hass.services.call('recorder', 'purge',
|
self.hass.services.call('recorder', 'purge',
|
||||||
service_data=service_data)
|
service_data=service_data)
|
||||||
self.hass.async_block_till_done()
|
self.hass.block_till_done()
|
||||||
self.hass.data[DATA_INSTANCE].block_till_done()
|
self.hass.data[DATA_INSTANCE].block_till_done()
|
||||||
self.assertTrue(self.hass.data[DATA_INSTANCE].did_vacuum)
|
self.assertTrue(self.hass.data[DATA_INSTANCE].did_vacuum)
|
||||||
|
|
|
@ -22,20 +22,20 @@ def test_validate_python(mock_exit):
|
||||||
mock_exit.reset_mock()
|
mock_exit.reset_mock()
|
||||||
|
|
||||||
with patch('sys.version_info',
|
with patch('sys.version_info',
|
||||||
new_callable=PropertyMock(return_value=(3, 4, 1))):
|
new_callable=PropertyMock(return_value=(3, 4, 2))):
|
||||||
main.validate_python()
|
main.validate_python()
|
||||||
assert mock_exit.called is True
|
assert mock_exit.called is True
|
||||||
|
|
||||||
mock_exit.reset_mock()
|
mock_exit.reset_mock()
|
||||||
|
|
||||||
with patch('sys.version_info',
|
with patch('sys.version_info',
|
||||||
new_callable=PropertyMock(return_value=(3, 4, 2))):
|
new_callable=PropertyMock(return_value=(3, 5, 2))):
|
||||||
main.validate_python()
|
main.validate_python()
|
||||||
assert mock_exit.called is False
|
assert mock_exit.called is True
|
||||||
|
|
||||||
mock_exit.reset_mock()
|
mock_exit.reset_mock()
|
||||||
|
|
||||||
with patch('sys.version_info',
|
with patch('sys.version_info',
|
||||||
new_callable=PropertyMock(return_value=(3, 5, 1))):
|
new_callable=PropertyMock(return_value=(3, 5, 3))):
|
||||||
main.validate_python()
|
main.validate_python()
|
||||||
assert mock_exit.called is False
|
assert mock_exit.called is False
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue