From 02238b6412ce271010b4227f45344173b9f525ea Mon Sep 17 00:00:00 2001
From: Andrey <andrey-git@users.noreply.github.com>
Date: Sat, 7 Jul 2018 17:48:02 +0300
Subject: [PATCH] Add python 3.7 to travis and tox (#14523)

* Add python 3.7 to travis and tox

* Use pyyaml from github

* Don't version constraints

* Fix version tag

* Change to new pyyaml release

* Python 3.7 requires xenial

* Fix namespace detection

* Use correct RegEx type

* Update pexpect to 4.6

* Use correct validation for dictionaries

* Disable Py37 incompatible packages

* Upgrade all pexpect to 4.6

* Add explicit None as default param
---
 .travis.yml                                             | 5 +++--
 homeassistant/components/device_tracker/aruba.py        | 2 +-
 homeassistant/components/device_tracker/asuswrt.py      | 2 +-
 homeassistant/components/device_tracker/cisco_ios.py    | 2 +-
 homeassistant/components/device_tracker/unifi_direct.py | 2 +-
 homeassistant/components/media_player/pandora.py        | 2 +-
 homeassistant/components/vacuum/__init__.py             | 2 +-
 homeassistant/loader.py                                 | 3 ++-
 homeassistant/package_constraints.txt                   | 2 +-
 requirements_all.txt                                    | 4 ++--
 requirements_test_all.txt                               | 2 +-
 setup.py                                                | 2 +-
 tests/components/mqtt/test_server.py                    | 6 ++++++
 tests/components/sensor/test_geo_rss_events.py          | 6 ++++++
 tests/test_util/aiohttp.py                              | 6 ++++--
 tox.ini                                                 | 2 +-
 16 files changed, 33 insertions(+), 17 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index b089d3f89be..5b3c43ec8c8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,8 +16,9 @@ matrix:
       env: TOXENV=py35
     - python: "3.6"
       env: TOXENV=py36
-    # - python: "3.6-dev"
-    #   env: TOXENV=py36
+    - python: "3.7"
+      env: TOXENV=py37
+      dist: xenial
   # allow_failures:
   #   - python: "3.5"
   #     env: TOXENV=typing
diff --git a/homeassistant/components/device_tracker/aruba.py b/homeassistant/components/device_tracker/aruba.py
index 92ef78f60f3..61eee99e721 100644
--- a/homeassistant/components/device_tracker/aruba.py
+++ b/homeassistant/components/device_tracker/aruba.py
@@ -16,7 +16,7 @@ from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
 
 _LOGGER = logging.getLogger(__name__)
 
-REQUIREMENTS = ['pexpect==4.0.1']
+REQUIREMENTS = ['pexpect==4.6.0']
 
 _DEVICES_REGEX = re.compile(
     r'(?P<name>([^\s]+)?)\s+' +
diff --git a/homeassistant/components/device_tracker/asuswrt.py b/homeassistant/components/device_tracker/asuswrt.py
index 5cb7e283c99..bea02143d72 100644
--- a/homeassistant/components/device_tracker/asuswrt.py
+++ b/homeassistant/components/device_tracker/asuswrt.py
@@ -19,7 +19,7 @@ from homeassistant.const import (
     CONF_HOST, CONF_PASSWORD, CONF_USERNAME, CONF_PORT, CONF_MODE,
     CONF_PROTOCOL)
 
-REQUIREMENTS = ['pexpect==4.0.1']
+REQUIREMENTS = ['pexpect==4.6.0']
 
 _LOGGER = logging.getLogger(__name__)
 
diff --git a/homeassistant/components/device_tracker/cisco_ios.py b/homeassistant/components/device_tracker/cisco_ios.py
index c13f622c5bf..1afea2c1607 100644
--- a/homeassistant/components/device_tracker/cisco_ios.py
+++ b/homeassistant/components/device_tracker/cisco_ios.py
@@ -16,7 +16,7 @@ from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, \
 
 _LOGGER = logging.getLogger(__name__)
 
-REQUIREMENTS = ['pexpect==4.0.1']
+REQUIREMENTS = ['pexpect==4.6.0']
 
 PLATFORM_SCHEMA = vol.All(
     PLATFORM_SCHEMA.extend({
diff --git a/homeassistant/components/device_tracker/unifi_direct.py b/homeassistant/components/device_tracker/unifi_direct.py
index c3c4a48bb82..228443fe22b 100644
--- a/homeassistant/components/device_tracker/unifi_direct.py
+++ b/homeassistant/components/device_tracker/unifi_direct.py
@@ -16,7 +16,7 @@ from homeassistant.const import (
     CONF_HOST, CONF_PASSWORD, CONF_USERNAME,
     CONF_PORT)
 
-REQUIREMENTS = ['pexpect==4.0.1']
+REQUIREMENTS = ['pexpect==4.6.0']
 
 _LOGGER = logging.getLogger(__name__)
 
diff --git a/homeassistant/components/media_player/pandora.py b/homeassistant/components/media_player/pandora.py
index a47db7f633c..90638cd9dfc 100644
--- a/homeassistant/components/media_player/pandora.py
+++ b/homeassistant/components/media_player/pandora.py
@@ -22,7 +22,7 @@ from homeassistant.const import (STATE_OFF, STATE_PAUSED, STATE_PLAYING,
                                  STATE_IDLE)
 from homeassistant import util
 
-REQUIREMENTS = ['pexpect==4.0.1']
+REQUIREMENTS = ['pexpect==4.6.0']
 _LOGGER = logging.getLogger(__name__)
 
 # SUPPORT_VOLUME_SET is close to available but we need volume up/down
diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py
index 1b7d5685231..880b3604a86 100644
--- a/homeassistant/components/vacuum/__init__.py
+++ b/homeassistant/components/vacuum/__init__.py
@@ -57,7 +57,7 @@ VACUUM_SET_FAN_SPEED_SERVICE_SCHEMA = VACUUM_SERVICE_SCHEMA.extend({
 
 VACUUM_SEND_COMMAND_SERVICE_SCHEMA = VACUUM_SERVICE_SCHEMA.extend({
     vol.Required(ATTR_COMMAND): cv.string,
-    vol.Optional(ATTR_PARAMS): vol.Any(cv.Dict, cv.ensure_list),
+    vol.Optional(ATTR_PARAMS): vol.Any(dict, cv.ensure_list),
 })
 
 SERVICE_TO_METHOD = {
diff --git a/homeassistant/loader.py b/homeassistant/loader.py
index 9e5efffdccb..153d00f92fc 100644
--- a/homeassistant/loader.py
+++ b/homeassistant/loader.py
@@ -87,7 +87,8 @@ def get_component(hass, comp_or_platform) -> Optional[ModuleType]:
             # This prevents that when only
             # custom_components/switch/some_platform.py exists,
             # the import custom_components.switch would succeed.
-            if module.__spec__ and module.__spec__.origin == 'namespace':
+            # __file__ was unset for namespaces before Python 3.7
+            if getattr(module, '__file__', None) is None:
                 continue
 
             _LOGGER.info("Loaded %s from %s", comp_or_platform, path)
diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt
index 32374b90135..66b17cf9bd9 100644
--- a/homeassistant/package_constraints.txt
+++ b/homeassistant/package_constraints.txt
@@ -6,7 +6,7 @@ certifi>=2018.04.16
 jinja2>=2.10
 pip>=8.0.3
 pytz>=2018.04
-pyyaml>=3.11,<4
+pyyaml>=3.13,<4
 requests==2.19.1
 voluptuous==0.11.1
 
diff --git a/requirements_all.txt b/requirements_all.txt
index b6c1bb7dc41..1949e52200b 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -7,7 +7,7 @@ certifi>=2018.04.16
 jinja2>=2.10
 pip>=8.0.3
 pytz>=2018.04
-pyyaml>=3.11,<4
+pyyaml>=3.13,<4
 requests==2.19.1
 voluptuous==0.11.1
 
@@ -630,7 +630,7 @@ pdunehd==1.3
 # homeassistant.components.device_tracker.cisco_ios
 # homeassistant.components.device_tracker.unifi_direct
 # homeassistant.components.media_player.pandora
-pexpect==4.0.1
+pexpect==4.6.0
 
 # homeassistant.components.rpi_pfio
 pifacecommon==4.1.2
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 0f7e5a264ee..2fb7153efb5 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -113,7 +113,7 @@ paho-mqtt==1.3.1
 # homeassistant.components.device_tracker.cisco_ios
 # homeassistant.components.device_tracker.unifi_direct
 # homeassistant.components.media_player.pandora
-pexpect==4.0.1
+pexpect==4.6.0
 
 # homeassistant.components.pilight
 pilight==0.1.1
diff --git a/setup.py b/setup.py
index 928d894c9d1..bbf10dd309d 100755
--- a/setup.py
+++ b/setup.py
@@ -40,7 +40,7 @@ REQUIRES = [
     'jinja2>=2.10',
     'pip>=8.0.3',
     'pytz>=2018.04',
-    'pyyaml>=3.11,<4',
+    'pyyaml>=3.13,<4',
     'requests==2.19.1',
     'voluptuous==0.11.1',
 ]
diff --git a/tests/components/mqtt/test_server.py b/tests/components/mqtt/test_server.py
index 9b4c0c69ac6..1c37c9049f3 100644
--- a/tests/components/mqtt/test_server.py
+++ b/tests/components/mqtt/test_server.py
@@ -1,5 +1,8 @@
 """The tests for the MQTT component embedded server."""
 from unittest.mock import Mock, MagicMock, patch
+import sys
+
+import pytest
 
 from homeassistant.setup import setup_component
 import homeassistant.components.mqtt as mqtt
@@ -7,6 +10,9 @@ import homeassistant.components.mqtt as mqtt
 from tests.common import get_test_home_assistant, mock_coro
 
 
+# Until https://github.com/beerfactory/hbmqtt/pull/139 is released
+@pytest.mark.skipif(sys.version_info[:2] >= (3, 7),
+                    reason='Package incompatible with Python 3.7')
 class TestMQTT:
     """Test the MQTT component."""
 
diff --git a/tests/components/sensor/test_geo_rss_events.py b/tests/components/sensor/test_geo_rss_events.py
index f9ec83cc8be..cc57c801430 100644
--- a/tests/components/sensor/test_geo_rss_events.py
+++ b/tests/components/sensor/test_geo_rss_events.py
@@ -1,7 +1,10 @@
 """The test for the geo rss events sensor platform."""
 import unittest
 from unittest import mock
+import sys
+
 import feedparser
+import pytest
 
 from homeassistant.setup import setup_component
 from tests.common import load_fixture, get_test_home_assistant
@@ -22,6 +25,9 @@ VALID_CONFIG_WITHOUT_CATEGORIES = {
 }
 
 
+# Until https://github.com/kurtmckee/feedparser/pull/131 is released.
+@pytest.mark.skipif(sys.version_info[:2] >= (3, 7),
+                    reason='Package incompatible with Python 3.7')
 class TestGeoRssServiceUpdater(unittest.TestCase):
     """Test the GeoRss service updater."""
 
diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py
index e67d5de50d1..0296b8c2fba 100644
--- a/tests/test_util/aiohttp.py
+++ b/tests/test_util/aiohttp.py
@@ -11,6 +11,8 @@ from yarl import URL
 
 from aiohttp.client_exceptions import ClientResponseError
 
+retype = type(re.compile(''))
+
 
 class AiohttpClientMocker:
     """Mock Aiohttp client requests."""
@@ -40,7 +42,7 @@ class AiohttpClientMocker:
         if content is None:
             content = b''
 
-        if not isinstance(url, re._pattern_type):
+        if not isinstance(url, retype):
             url = URL(url)
         if params:
                 url = url.with_query(params)
@@ -146,7 +148,7 @@ class AiohttpClientMockResponse:
             return False
 
         # regular expression matching
-        if isinstance(self._url, re._pattern_type):
+        if isinstance(self._url, retype):
             return self._url.search(str(url)) is not None
 
         if (self._url.scheme != url.scheme or self._url.host != url.host or
diff --git a/tox.ini b/tox.ini
index ca82c83d0fc..578a431febf 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py35, py36, lint, pylint, typing
+envlist = py35, py36, py37, lint, pylint, typing
 skip_missing_interpreters = True
 
 [testenv]
-- 
GitLab