From 7d2717dd71c05c5f9daf749e77d284d95d12d509 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 22 Feb 2018 15:39:13 -0800 Subject: [PATCH 1/4] Hello Python 3.5 --- .travis.yml | 6 ++---- homeassistant/__main__.py | 8 +------- homeassistant/const.py | 3 +-- homeassistant/core.py | 36 ++++++++++++++++-------------------- setup.py | 7 +------ 5 files changed, 21 insertions(+), 39 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3d6789ea58616..027c1f25c626c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,12 +6,10 @@ addons: matrix: fast_finish: true include: - - python: "3.4.2" + - python: "3.5.3" env: TOXENV=lint - - python: "3.4.2" + - python: "3.5.3" env: TOXENV=pylint - - python: "3.4.2" - env: TOXENV=py34 # - python: "3.5" # env: TOXENV=typing - python: "3.5.3" diff --git a/homeassistant/__main__.py b/homeassistant/__main__.py index 1cf6ecf7b9870..319d00e6de55a 100644 --- a/homeassistant/__main__.py +++ b/homeassistant/__main__.py @@ -15,7 +15,6 @@ __version__, EVENT_HOMEASSISTANT_START, REQUIRED_PYTHON_VER, - REQUIRED_PYTHON_VER_WIN, RESTART_EXIT_CODE, ) @@ -33,12 +32,7 @@ def attempt_use_uvloop(): def validate_python() -> None: """Validate that the right Python version is running.""" - if sys.platform == "win32" and \ - 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: + if sys.version_info[:3] < REQUIRED_PYTHON_VER: print("Home Assistant requires at least Python {}.{}.{}".format( *REQUIRED_PYTHON_VER)) sys.exit(1) diff --git a/homeassistant/const.py b/homeassistant/const.py index c1ced11da7862..1d90e5307026c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -5,8 +5,7 @@ PATCH_VERSION = '0.dev0' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) -REQUIRED_PYTHON_VER = (3, 4, 2) -REQUIRED_PYTHON_VER_WIN = (3, 5, 2) +REQUIRED_PYTHON_VER = (3, 5, 3) # Format for platforms PLATFORM_FORMAT = '{}.{}' diff --git a/homeassistant/core.py b/homeassistant/core.py index b1cf9c51efdeb..8ff9d9cfd81d9 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -164,8 +164,7 @@ def start(self) -> None: finally: self.loop.close() - @asyncio.coroutine - def async_start(self): + async def async_start(self): """Finalize startup from inside the event loop. This method is a coroutine. @@ -181,7 +180,7 @@ def async_start(self): # Only block for EVENT_HOMEASSISTANT_START listener self.async_stop_track_tasks() with timeout(TIMEOUT_EVENT_START, loop=self.loop): - yield from self.async_block_till_done() + await self.async_block_till_done() except asyncio.TimeoutError: _LOGGER.warning( 'Something is blocking Home Assistant from wrapping up the ' @@ -190,7 +189,7 @@ def async_start(self): ', '.join(self.config.components)) # 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 _async_create_timer(self) @@ -259,27 +258,25 @@ def block_till_done(self) -> None: run_coroutine_threadsafe( self.async_block_till_done(), loop=self.loop).result() - @asyncio.coroutine - def async_block_till_done(self): + async def async_block_till_done(self): """Block till all pending work is done.""" # 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: pending = [task for task in self._pending_tasks if not task.done()] self._pending_tasks.clear() if pending: - yield from asyncio.wait(pending, loop=self.loop) + await asyncio.wait(pending, loop=self.loop) else: - yield from asyncio.sleep(0, loop=self.loop) + await asyncio.sleep(0, loop=self.loop) def stop(self) -> None: """Stop Home Assistant and shuts down all threads.""" fire_coroutine_threadsafe(self.async_stop(), self.loop) - @asyncio.coroutine - def async_stop(self, exit_code=0) -> None: + async def async_stop(self, exit_code=0) -> None: """Stop Home Assistant and shuts down all threads. This method is a coroutine. @@ -288,12 +285,12 @@ def async_stop(self, exit_code=0) -> None: self.state = CoreState.stopping self.async_track_tasks() self.bus.async_fire(EVENT_HOMEASSISTANT_STOP) - yield from self.async_block_till_done() + await self.async_block_till_done() # stage 2 self.state = CoreState.not_running self.bus.async_fire(EVENT_HOMEASSISTANT_CLOSE) - yield from self.async_block_till_done() + await self.async_block_till_done() self.executor.shutdown() self.exit_code = exit_code @@ -912,8 +909,8 @@ def call(self, domain, service, service_data=None, blocking=False): self._hass.loop ).result() - @asyncio.coroutine - def async_call(self, domain, service, service_data=None, blocking=False): + async def async_call(self, domain, service, service_data=None, + blocking=False): """ Call a service. @@ -956,14 +953,13 @@ def service_executed(event): self._hass.bus.async_fire(EVENT_CALL_SERVICE, event_data) if blocking: - done, _ = yield from asyncio.wait( + done, _ = await asyncio.wait( [fut], loop=self._hass.loop, timeout=SERVICE_CALL_LIMIT) success = bool(done) unsub() return success - @asyncio.coroutine - def _event_to_service_call(self, event): + async def _event_to_service_call(self, event): """Handle the SERVICE_CALLED events from the EventBus.""" service_data = event.data.get(ATTR_SERVICE_DATA) or {} domain = event.data.get(ATTR_DOMAIN).lower() @@ -1007,7 +1003,7 @@ def fire_service_executed(): service_handler.func(service_call) fire_service_executed() elif service_handler.is_coroutinefunction: - yield from service_handler.func(service_call) + await service_handler.func(service_call) fire_service_executed() else: def execute_service(): @@ -1015,7 +1011,7 @@ def execute_service(): service_handler.func(service_call) 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 _LOGGER.exception('Error executing service %s', service_call) diff --git a/setup.py b/setup.py index bca49d33647d1..c3cf07223bc51 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 """Home Assistant setup script.""" -import sys - from setuptools import setup, find_packages import homeassistant.const as hass_const @@ -27,7 +25,6 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Topic :: Home Automation' @@ -64,9 +61,7 @@ MIN_PY_VERSION = '.'.join(map( str, - hass_const.REQUIRED_PYTHON_VER_WIN - if sys.platform.startswith('win') - else hass_const.REQUIRED_PYTHON_VER)) + hass_const.REQUIRED_PYTHON_VER)) setup( name=PROJECT_PACKAGE_NAME, From a6b27cc6a4faf92dc005df65c6cc41be15b3aeba Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 22 Feb 2018 15:55:16 -0800 Subject: [PATCH 2/4] Fix test --- tests/test_main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index d3bd3cf751b1a..4518146c8cff3 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -22,20 +22,20 @@ def test_validate_python(mock_exit): mock_exit.reset_mock() with patch('sys.version_info', - new_callable=PropertyMock(return_value=(3, 4, 1))): + new_callable=PropertyMock(return_value=(3, 4, 2))): main.validate_python() assert mock_exit.called is True mock_exit.reset_mock() with patch('sys.version_info', - new_callable=PropertyMock(return_value=(3, 4, 2))): + new_callable=PropertyMock(return_value=(3, 5, 2))): main.validate_python() - assert mock_exit.called is False + assert mock_exit.called is True mock_exit.reset_mock() with patch('sys.version_info', - new_callable=PropertyMock(return_value=(3, 5, 1))): + new_callable=PropertyMock(return_value=(3, 5, 3))): main.validate_python() assert mock_exit.called is False From 94bad92d20481329238cf88e649d6859b55412a2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 22 Feb 2018 16:04:14 -0800 Subject: [PATCH 3/4] Fix tests --- tests/components/emulated_hue/test_hue_api.py | 10 +++++----- tests/components/mqtt/test_discovery.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/components/emulated_hue/test_hue_api.py b/tests/components/emulated_hue/test_hue_api.py index cba3c83576316..91988a76212a1 100644 --- a/tests/components/emulated_hue/test_hue_api.py +++ b/tests/components/emulated_hue/test_hue_api.py @@ -420,11 +420,11 @@ def test_proper_put_state_request(hue_client): # pylint: disable=invalid-name -def perform_put_test_on_ceiling_lights(hass_hue, hue_client, - content_type='application/json'): +async def perform_put_test_on_ceiling_lights(hass_hue, hue_client, + content_type='application/json'): """Test the setting of a light.""" # 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, {const.ATTR_ENTITY_ID: 'light.ceiling_lights'}, blocking=True) @@ -433,14 +433,14 @@ def perform_put_test_on_ceiling_lights(hass_hue, hue_client, assert ceiling_lights.state == STATE_OFF # 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, 'light.ceiling_lights', True, 56, content_type) assert office_result.status == 200 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 diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index 995f7e891f9a1..1dd29909ffdfb 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -21,8 +21,8 @@ def test_subscribing_config_topic(hass, mqtt_mock): assert call_args[2] == 0 -@asyncio.coroutine @patch('homeassistant.components.mqtt.discovery.async_load_platform') +@asyncio.coroutine def test_invalid_topic(mock_load_platform, hass, mqtt_mock): """Test sending to invalid topic.""" 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 -@asyncio.coroutine @patch('homeassistant.components.mqtt.discovery.async_load_platform') +@asyncio.coroutine def test_invalid_json(mock_load_platform, hass, mqtt_mock, caplog): """Test sending in invalid JSON.""" 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 -@asyncio.coroutine @patch('homeassistant.components.mqtt.discovery.async_load_platform') +@asyncio.coroutine def test_only_valid_components(mock_load_platform, hass, mqtt_mock, caplog): """Test for a valid component.""" mock_load_platform.return_value = mock_coro() From 3f13bcc11383afb7318c823c9470ca3a5ce2fb65 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 22 Feb 2018 16:05:14 -0800 Subject: [PATCH 4/4] Fix never awaited block till done warnings --- tests/components/device_tracker/test_automatic.py | 2 -- tests/components/recorder/test_purge.py | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/components/device_tracker/test_automatic.py b/tests/components/device_tracker/test_automatic.py index d90b5c0dd6216..84ea84cdcadd2 100644 --- a/tests/components/device_tracker/test_automatic.py +++ b/tests/components/device_tracker/test_automatic.py @@ -114,8 +114,6 @@ def ws_connect(): result = hass.loop.run_until_complete( async_setup_scanner(hass, config, mock_see)) - hass.async_block_till_done() - assert result assert mock_create_session.called diff --git a/tests/components/recorder/test_purge.py b/tests/components/recorder/test_purge.py index 2ae039b67125d..7bac7c25e7e7b 100644 --- a/tests/components/recorder/test_purge.py +++ b/tests/components/recorder/test_purge.py @@ -165,7 +165,7 @@ def test_purge_method(self): # run purge method - no service data, use defaults self.hass.services.call('recorder', 'purge') - self.hass.async_block_till_done() + self.hass.block_till_done() # Small wait for recorder thread self.hass.data[DATA_INSTANCE].block_till_done() @@ -177,7 +177,7 @@ def test_purge_method(self): # run purge method - correct service data self.hass.services.call('recorder', 'purge', service_data=service_data) - self.hass.async_block_till_done() + self.hass.block_till_done() # Small wait for recorder thread self.hass.data[DATA_INSTANCE].block_till_done() @@ -203,6 +203,6 @@ def test_purge_method(self): self.assertFalse(self.hass.data[DATA_INSTANCE].did_vacuum) self.hass.services.call('recorder', 'purge', service_data=service_data) - self.hass.async_block_till_done() + self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() self.assertTrue(self.hass.data[DATA_INSTANCE].did_vacuum)