mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 04:37:06 +00:00
Drop Python 3.6 support (#29978)
This commit is contained in:
parent
bfafa77016
commit
445fd15f76
@ -5,7 +5,6 @@ omit =
|
|||||||
homeassistant/__main__.py
|
homeassistant/__main__.py
|
||||||
homeassistant/helpers/signal.py
|
homeassistant/helpers/signal.py
|
||||||
homeassistant/helpers/typing.py
|
homeassistant/helpers/typing.py
|
||||||
homeassistant/monkey_patch.py
|
|
||||||
homeassistant/scripts/*.py
|
homeassistant/scripts/*.py
|
||||||
homeassistant/util/async.py
|
homeassistant/util/async.py
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ build:
|
|||||||
image: latest
|
image: latest
|
||||||
|
|
||||||
python:
|
python:
|
||||||
version: 3.6
|
version: 3.7
|
||||||
setup_py_install: true
|
setup_py_install: true
|
||||||
|
|
||||||
requirements_file: requirements_docs.txt
|
requirements_file: requirements_docs.txt
|
||||||
|
10
.travis.yml
10
.travis.yml
@ -14,15 +14,13 @@ addons:
|
|||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
include:
|
include:
|
||||||
- python: "3.6.1"
|
- python: "3.7.0"
|
||||||
env: TOXENV=lint
|
env: TOXENV=lint
|
||||||
- python: "3.6.1"
|
- python: "3.7.0"
|
||||||
env: TOXENV=pylint PYLINT_ARGS=--jobs=0 TRAVIS_WAIT=30
|
env: TOXENV=pylint PYLINT_ARGS=--jobs=0 TRAVIS_WAIT=30
|
||||||
- python: "3.6.1"
|
- python: "3.7.0"
|
||||||
env: TOXENV=typing
|
env: TOXENV=typing
|
||||||
- python: "3.6.1"
|
- python: "3.7.0"
|
||||||
env: TOXENV=py36
|
|
||||||
- python: "3.7"
|
|
||||||
env: TOXENV=py37
|
env: TOXENV=py37
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
|
@ -14,8 +14,6 @@ pr:
|
|||||||
|
|
||||||
resources:
|
resources:
|
||||||
containers:
|
containers:
|
||||||
- container: 36
|
|
||||||
image: homeassistant/ci-azure:3.6
|
|
||||||
- container: 37
|
- container: 37
|
||||||
image: homeassistant/ci-azure:3.7
|
image: homeassistant/ci-azure:3.7
|
||||||
repositories:
|
repositories:
|
||||||
@ -25,7 +23,7 @@ resources:
|
|||||||
endpoint: 'home-assistant'
|
endpoint: 'home-assistant'
|
||||||
variables:
|
variables:
|
||||||
- name: PythonMain
|
- name: PythonMain
|
||||||
value: '36'
|
value: '37'
|
||||||
- group: codecov
|
- group: codecov
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
@ -108,8 +106,6 @@ stages:
|
|||||||
strategy:
|
strategy:
|
||||||
maxParallel: 3
|
maxParallel: 3
|
||||||
matrix:
|
matrix:
|
||||||
Python36:
|
|
||||||
python.container: '36'
|
|
||||||
Python37:
|
Python37:
|
||||||
python.container: '37'
|
python.container: '37'
|
||||||
container: $[ variables['python.container'] ]
|
container: $[ variables['python.container'] ]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Start Home Assistant."""
|
"""Start Home Assistant."""
|
||||||
import argparse
|
import argparse
|
||||||
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -7,7 +8,6 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List
|
from typing import TYPE_CHECKING, Any, Dict, List
|
||||||
|
|
||||||
from homeassistant import monkey_patch
|
|
||||||
from homeassistant.const import REQUIRED_PYTHON_VER, RESTART_EXIT_CODE, __version__
|
from homeassistant.const import REQUIRED_PYTHON_VER, RESTART_EXIT_CODE, __version__
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -16,7 +16,6 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
def set_loop() -> None:
|
def set_loop() -> None:
|
||||||
"""Attempt to use different loop."""
|
"""Attempt to use different loop."""
|
||||||
import asyncio
|
|
||||||
from asyncio.events import BaseDefaultEventLoopPolicy
|
from asyncio.events import BaseDefaultEventLoopPolicy
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
@ -345,11 +344,6 @@ def main() -> int:
|
|||||||
"""Start Home Assistant."""
|
"""Start Home Assistant."""
|
||||||
validate_python()
|
validate_python()
|
||||||
|
|
||||||
monkey_patch_needed = sys.version_info[:3] < (3, 6, 3)
|
|
||||||
if monkey_patch_needed and os.environ.get("HASS_NO_MONKEY") != "1":
|
|
||||||
monkey_patch.disable_c_asyncio()
|
|
||||||
monkey_patch.patch_weakref_tasks()
|
|
||||||
|
|
||||||
set_loop()
|
set_loop()
|
||||||
|
|
||||||
# Run a simple daemon runner process on Windows to handle restarts
|
# Run a simple daemon runner process on Windows to handle restarts
|
||||||
@ -383,13 +377,11 @@ def main() -> int:
|
|||||||
if args.pid_file:
|
if args.pid_file:
|
||||||
write_pid(args.pid_file)
|
write_pid(args.pid_file)
|
||||||
|
|
||||||
from homeassistant.util.async_ import asyncio_run
|
exit_code = asyncio.run(setup_and_run_hass(config_dir, args))
|
||||||
|
|
||||||
exit_code = asyncio_run(setup_and_run_hass(config_dir, args))
|
|
||||||
if exit_code == RESTART_EXIT_CODE and not args.runner:
|
if exit_code == RESTART_EXIT_CODE and not args.runner:
|
||||||
try_to_restart()
|
try_to_restart()
|
||||||
|
|
||||||
return exit_code # type: ignore
|
return exit_code
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -4,10 +4,10 @@ MINOR_VERSION = 104
|
|||||||
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, 6, 1)
|
REQUIRED_PYTHON_VER = (3, 7, 0)
|
||||||
# Truthy date string triggers showing related deprecation warning messages.
|
# Truthy date string triggers showing related deprecation warning messages.
|
||||||
REQUIRED_NEXT_PYTHON_VER = (3, 7, 0)
|
REQUIRED_NEXT_PYTHON_VER = (3, 8, 0)
|
||||||
REQUIRED_NEXT_PYTHON_DATE = "December 15, 2019"
|
REQUIRED_NEXT_PYTHON_DATE = ""
|
||||||
|
|
||||||
# Format for platform files
|
# Format for platform files
|
||||||
PLATFORM_FORMAT = "{platform}.{domain}"
|
PLATFORM_FORMAT = "{platform}.{domain}"
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
"""Monkey patch Python to work around issues causing segfaults.
|
|
||||||
|
|
||||||
Under heavy threading operations that schedule calls into
|
|
||||||
the asyncio event loop, Task objects are created. Due to
|
|
||||||
a bug in Python, GC may have an issue when switching between
|
|
||||||
the threads and objects with __del__ (which various components
|
|
||||||
in HASS have).
|
|
||||||
|
|
||||||
This monkey-patch removes the weakref.Weakset, and replaces it
|
|
||||||
with an object that ignores the only call utilizing it (the
|
|
||||||
Task.__init__ which calls _all_tasks.add(self)). It also removes
|
|
||||||
the __del__ which could trigger the future objects __del__ at
|
|
||||||
unpredictable times.
|
|
||||||
|
|
||||||
The side-effect of this manipulation of the Task is that
|
|
||||||
Task.all_tasks() is no longer accurate, and there will be no
|
|
||||||
warning emitted if a Task is GC'd while in use.
|
|
||||||
|
|
||||||
Related Python bugs:
|
|
||||||
- https://bugs.python.org/issue26617
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
|
||||||
def patch_weakref_tasks() -> None:
|
|
||||||
"""Replace weakref.WeakSet to address Python 3 bug."""
|
|
||||||
# pylint: disable=no-self-use, protected-access
|
|
||||||
import asyncio.tasks
|
|
||||||
|
|
||||||
class IgnoreCalls:
|
|
||||||
"""Ignore add calls."""
|
|
||||||
|
|
||||||
def add(self, other: Any) -> None:
|
|
||||||
"""No-op add."""
|
|
||||||
return
|
|
||||||
|
|
||||||
asyncio.tasks.Task._all_tasks = IgnoreCalls() # type: ignore
|
|
||||||
try:
|
|
||||||
del asyncio.tasks.Task.__del__
|
|
||||||
except: # noqa: E722 pylint: disable=bare-except
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def disable_c_asyncio() -> None:
|
|
||||||
"""Disable using C implementation of asyncio.
|
|
||||||
|
|
||||||
Required to be able to apply the weakref monkey patch.
|
|
||||||
|
|
||||||
Requires Python 3.6+.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class AsyncioImportFinder:
|
|
||||||
"""Finder that blocks C version of asyncio being loaded."""
|
|
||||||
|
|
||||||
PATH_TRIGGER = "_asyncio"
|
|
||||||
|
|
||||||
def __init__(self, path_entry: str) -> None:
|
|
||||||
if path_entry != self.PATH_TRIGGER:
|
|
||||||
raise ImportError()
|
|
||||||
|
|
||||||
def find_module(self, fullname: str, path: Any = None) -> None:
|
|
||||||
"""Find a module."""
|
|
||||||
if fullname == self.PATH_TRIGGER:
|
|
||||||
raise ModuleNotFoundError()
|
|
||||||
|
|
||||||
sys.path_hooks.append(AsyncioImportFinder)
|
|
||||||
sys.path.insert(0, AsyncioImportFinder.PATH_TRIGGER)
|
|
||||||
|
|
||||||
try:
|
|
||||||
import _asyncio # noqa: F401
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
@ -7,7 +7,6 @@ async_timeout==3.0.1
|
|||||||
attrs==19.3.0
|
attrs==19.3.0
|
||||||
bcrypt==3.1.7
|
bcrypt==3.1.7
|
||||||
certifi>=2019.11.28
|
certifi>=2019.11.28
|
||||||
contextvars==2.4;python_version<"3.7"
|
|
||||||
cryptography==2.8
|
cryptography==2.8
|
||||||
defusedxml==0.6.0
|
defusedxml==0.6.0
|
||||||
distro==1.4.0
|
distro==1.4.0
|
||||||
@ -29,7 +28,7 @@ zeroconf==0.24.0
|
|||||||
|
|
||||||
pycryptodome>=3.6.6
|
pycryptodome>=3.6.6
|
||||||
|
|
||||||
# Breaks Python 3.6 and is not needed for our supported Python versions
|
# Not needed for our supported Python versions
|
||||||
enum34==1000000000.0.0
|
enum34==1000000000.0.0
|
||||||
|
|
||||||
# This is a old unmaintained library and is replaced with pycryptodome
|
# This is a old unmaintained library and is replaced with pycryptodome
|
||||||
|
@ -1,33 +1,14 @@
|
|||||||
"""Asyncio backports for Python 3.6 compatibility."""
|
"""Asyncio backports for Python 3.6 compatibility."""
|
||||||
import asyncio
|
|
||||||
from asyncio import coroutines, ensure_future
|
from asyncio import coroutines, ensure_future
|
||||||
from asyncio.events import AbstractEventLoop
|
from asyncio.events import AbstractEventLoop
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
from typing import Any, Awaitable, Callable, Coroutine, TypeVar
|
from typing import Any, Callable, Coroutine
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
# pylint: disable=invalid-name
|
|
||||||
asyncio_run = asyncio.run # type: ignore
|
|
||||||
except AttributeError:
|
|
||||||
_T = TypeVar("_T")
|
|
||||||
|
|
||||||
def asyncio_run(main: Awaitable[_T], *, debug: bool = False) -> _T:
|
|
||||||
"""Minimal re-implementation of asyncio.run (since 3.7)."""
|
|
||||||
loop = asyncio.new_event_loop()
|
|
||||||
asyncio.set_event_loop(loop)
|
|
||||||
loop.set_debug(debug)
|
|
||||||
try:
|
|
||||||
return loop.run_until_complete(main)
|
|
||||||
finally:
|
|
||||||
asyncio.set_event_loop(None)
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
|
|
||||||
def fire_coroutine_threadsafe(coro: Coroutine, loop: AbstractEventLoop) -> None:
|
def fire_coroutine_threadsafe(coro: Coroutine, loop: AbstractEventLoop) -> None:
|
||||||
"""Submit a coroutine object to a given event loop.
|
"""Submit a coroutine object to a given event loop.
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[tool.black]
|
[tool.black]
|
||||||
target-version = ["py36", "py37", "py38"]
|
target-version = ["py37", "py38"]
|
||||||
exclude = 'generated'
|
exclude = 'generated'
|
||||||
|
@ -5,7 +5,6 @@ async_timeout==3.0.1
|
|||||||
attrs==19.3.0
|
attrs==19.3.0
|
||||||
bcrypt==3.1.7
|
bcrypt==3.1.7
|
||||||
certifi>=2019.11.28
|
certifi>=2019.11.28
|
||||||
contextvars==2.4;python_version<"3.7"
|
|
||||||
importlib-metadata==0.23
|
importlib-metadata==0.23
|
||||||
jinja2>=2.10.3
|
jinja2>=2.10.3
|
||||||
PyJWT==1.7.1
|
PyJWT==1.7.1
|
||||||
|
@ -58,7 +58,7 @@ CONSTRAINT_PATH = os.path.join(
|
|||||||
CONSTRAINT_BASE = """
|
CONSTRAINT_BASE = """
|
||||||
pycryptodome>=3.6.6
|
pycryptodome>=3.6.6
|
||||||
|
|
||||||
# Breaks Python 3.6 and is not needed for our supported Python versions
|
# Not needed for our supported Python versions
|
||||||
enum34==1000000000.0.0
|
enum34==1000000000.0.0
|
||||||
|
|
||||||
# This is a old unmaintained library and is replaced with pycryptodome
|
# This is a old unmaintained library and is replaced with pycryptodome
|
||||||
|
@ -11,7 +11,6 @@ classifier =
|
|||||||
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.6
|
|
||||||
Programming Language :: Python :: 3.7
|
Programming Language :: Python :: 3.7
|
||||||
Topic :: Home Automation
|
Topic :: Home Automation
|
||||||
|
|
||||||
@ -57,7 +56,7 @@ forced_separate = tests
|
|||||||
combine_as_imports = true
|
combine_as_imports = true
|
||||||
|
|
||||||
[mypy]
|
[mypy]
|
||||||
python_version = 3.6
|
python_version = 3.7
|
||||||
ignore_errors = true
|
ignore_errors = true
|
||||||
follow_imports = silent
|
follow_imports = silent
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
@ -65,7 +64,7 @@ warn_incomplete_stub = true
|
|||||||
warn_redundant_casts = true
|
warn_redundant_casts = true
|
||||||
warn_unused_configs = true
|
warn_unused_configs = true
|
||||||
|
|
||||||
[mypy-homeassistant.bootstrap,homeassistant.components,homeassistant.config_entries,homeassistant.config,homeassistant.const,homeassistant.core,homeassistant.data_entry_flow,homeassistant.exceptions,homeassistant.loader,homeassistant.__main__,homeassistant.monkey_patch,homeassistant.requirements,homeassistant.setup,homeassistant.util,homeassistant.auth.*,homeassistant.components.automation.*,homeassistant.components.binary_sensor.*,homeassistant.components.calendar.*,homeassistant.components.cover.*,homeassistant.components.device_automation.*,homeassistant.components.frontend.*,homeassistant.components.geo_location.*,homeassistant.components.group.*,homeassistant.components.history.*,homeassistant.components.http.*,homeassistant.components.image_processing.*,homeassistant.components.integration.*,homeassistant.components.light.*,homeassistant.components.lock.*,homeassistant.components.mailbox.*,homeassistant.components.media_player.*,homeassistant.components.notify.*,homeassistant.components.persistent_notification.*,homeassistant.components.proximity.*,homeassistant.components.remote.*,homeassistant.components.scene.*,homeassistant.components.sensor.*,homeassistant.components.sun.*,homeassistant.components.switch.*,homeassistant.components.systemmonitor.*,homeassistant.components.tts.*,homeassistant.components.vacuum.*,homeassistant.components.water_heater.*,homeassistant.components.weather.*,homeassistant.components.websocket_api.*,homeassistant.components.zone.*,homeassistant.helpers.*,homeassistant.scripts.*,homeassistant.util.*]
|
[mypy-homeassistant.bootstrap,homeassistant.components,homeassistant.config_entries,homeassistant.config,homeassistant.const,homeassistant.core,homeassistant.data_entry_flow,homeassistant.exceptions,homeassistant.loader,homeassistant.__main__,homeassistant.requirements,homeassistant.setup,homeassistant.util,homeassistant.auth.*,homeassistant.components.automation.*,homeassistant.components.binary_sensor.*,homeassistant.components.calendar.*,homeassistant.components.cover.*,homeassistant.components.device_automation.*,homeassistant.components.frontend.*,homeassistant.components.geo_location.*,homeassistant.components.group.*,homeassistant.components.history.*,homeassistant.components.http.*,homeassistant.components.image_processing.*,homeassistant.components.integration.*,homeassistant.components.light.*,homeassistant.components.lock.*,homeassistant.components.mailbox.*,homeassistant.components.media_player.*,homeassistant.components.notify.*,homeassistant.components.persistent_notification.*,homeassistant.components.proximity.*,homeassistant.components.remote.*,homeassistant.components.scene.*,homeassistant.components.sensor.*,homeassistant.components.sun.*,homeassistant.components.switch.*,homeassistant.components.systemmonitor.*,homeassistant.components.tts.*,homeassistant.components.vacuum.*,homeassistant.components.water_heater.*,homeassistant.components.weather.*,homeassistant.components.websocket_api.*,homeassistant.components.zone.*,homeassistant.helpers.*,homeassistant.scripts.*,homeassistant.util.*]
|
||||||
ignore_errors = false
|
ignore_errors = false
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
disallow_incomplete_defs = true
|
disallow_incomplete_defs = true
|
||||||
|
1
setup.py
1
setup.py
@ -38,7 +38,6 @@ REQUIRES = [
|
|||||||
"attrs==19.3.0",
|
"attrs==19.3.0",
|
||||||
"bcrypt==3.1.7",
|
"bcrypt==3.1.7",
|
||||||
"certifi>=2019.11.28",
|
"certifi>=2019.11.28",
|
||||||
'contextvars==2.4;python_version<"3.7"',
|
|
||||||
"importlib-metadata==0.23",
|
"importlib-metadata==0.23",
|
||||||
"jinja2>=2.10.3",
|
"jinja2>=2.10.3",
|
||||||
"PyJWT==1.7.1",
|
"PyJWT==1.7.1",
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Tests for async util methods from Python source."""
|
"""Tests for async util methods from Python source."""
|
||||||
import asyncio
|
import asyncio
|
||||||
import sys
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
@ -112,11 +111,7 @@ class RunThreadsafeTests(TestCase):
|
|||||||
"""Wait 0.05 second and return a + b."""
|
"""Wait 0.05 second and return a + b."""
|
||||||
yield from asyncio.sleep(0.05, loop=self.loop)
|
yield from asyncio.sleep(0.05, loop=self.loop)
|
||||||
if cancel:
|
if cancel:
|
||||||
if sys.version_info[:2] >= (3, 7):
|
asyncio.current_task(self.loop).cancel()
|
||||||
current_task = asyncio.current_task
|
|
||||||
else:
|
|
||||||
current_task = asyncio.tasks.Task.current_task
|
|
||||||
current_task(self.loop).cancel()
|
|
||||||
yield
|
yield
|
||||||
return self.add_callback(a, b, fail, invalid)
|
return self.add_callback(a, b, fail, invalid)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user