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:
Paulus Schoutsen 2018-02-22 23:22:27 -08:00 committed by GitHub
parent 156206dfee
commit 6ee3c1b3e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 36 additions and 56 deletions

View file

@ -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"

View file

@ -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)

View file

@ -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 = '{}.{}'

View file

@ -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)

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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)

View file

@ -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