diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index fa5b52478d07..2fdbd60dd46a 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -31,6 +31,7 @@ Jinja2==3.1.2 lru-dict==1.2.0 mutagen==1.46.0 orjson==3.9.2 +packaging>=23.1 paho-mqtt==1.6.1 Pillow==10.0.0 pip>=21.3.1 diff --git a/homeassistant/runner.py b/homeassistant/runner.py index 67ec232db9c2..4bbf1a7dada0 100644 --- a/homeassistant/runner.py +++ b/homeassistant/runner.py @@ -11,6 +11,8 @@ import threading import traceback from typing import Any +import packaging.tags + from . import bootstrap from .core import callback from .helpers.frame import warn_use @@ -29,7 +31,6 @@ from .util.thread import deadlock_safe_shutdown # MAX_EXECUTOR_WORKERS = 64 TASK_CANCELATION_TIMEOUT = 5 -ALPINE_RELEASE_FILE = "/etc/alpine-release" _LOGGER = logging.getLogger(__name__) @@ -164,8 +165,9 @@ def _enable_posix_spawn() -> None: # The subprocess module does not know about Alpine Linux/musl # and will use fork() instead of posix_spawn() which significantly # less efficient. This is a workaround to force posix_spawn() - # on Alpine Linux which is supported by musl. - subprocess._USE_POSIX_SPAWN = os.path.exists(ALPINE_RELEASE_FILE) + # when using musl since cpython is not aware its supported. + tag = next(packaging.tags.sys_tags()) + subprocess._USE_POSIX_SPAWN = "musllinux" in tag.platform def run(runtime_config: RuntimeConfig) -> int: diff --git a/pyproject.toml b/pyproject.toml index 07d527eb77cd..9a5261879995 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ dependencies = [ # pyOpenSSL 23.2.0 is required to work with cryptography 41+ "pyOpenSSL==23.2.0", "orjson==3.9.2", + "packaging>=23.1", "pip>=21.3.1", "python-slugify==4.0.1", "PyYAML==6.0.1", diff --git a/requirements.txt b/requirements.txt index 4106a8515d14..debdc7dbcb3c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,6 +19,7 @@ PyJWT==2.8.0 cryptography==41.0.3 pyOpenSSL==23.2.0 orjson==3.9.2 +packaging>=23.1 pip>=21.3.1 python-slugify==4.0.1 PyYAML==6.0.1 diff --git a/tests/test_runner.py b/tests/test_runner.py index f32321c578c3..5fe5c2881ffd 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -1,8 +1,10 @@ """Test the runner.""" import asyncio +from collections.abc import Iterator import threading from unittest.mock import patch +import packaging.tags import py import pytest @@ -147,19 +149,22 @@ async def test_unhandled_exception_traceback( def test__enable_posix_spawn() -> None: - """Test that we can enable posix_spawn on Alpine.""" + """Test that we can enable posix_spawn on musllinux.""" - def _mock_alpine_exists(path): - return path == "/etc/alpine-release" + def _mock_sys_tags_any() -> Iterator[packaging.tags.Tag]: + yield from packaging.tags.parse_tag("py3-none-any") - with patch.object(runner.subprocess, "_USE_POSIX_SPAWN", False), patch.object( - runner.os.path, "exists", _mock_alpine_exists + def _mock_sys_tags_musl() -> Iterator[packaging.tags.Tag]: + yield from packaging.tags.parse_tag("cp311-cp311-musllinux_1_1_x86_64") + + with patch.object(runner.subprocess, "_USE_POSIX_SPAWN", False), patch( + "homeassistant.runner.packaging.tags.sys_tags", side_effect=_mock_sys_tags_musl ): runner._enable_posix_spawn() assert runner.subprocess._USE_POSIX_SPAWN is True - with patch.object(runner.subprocess, "_USE_POSIX_SPAWN", False), patch.object( - runner.os.path, "exists", return_value=False + with patch.object(runner.subprocess, "_USE_POSIX_SPAWN", False), patch( + "homeassistant.runner.packaging.tags.sys_tags", side_effect=_mock_sys_tags_any ): runner._enable_posix_spawn() assert runner.subprocess._USE_POSIX_SPAWN is False