Fix unifi_discovery deepcopy crash on Python 3.14 (#168153)

This commit is contained in:
Raphael Hehl
2026-04-14 16:31:04 +02:00
committed by GitHub
parent 2ba66fb722
commit 66b2d4477b
3 changed files with 59 additions and 3 deletions
@@ -2,6 +2,7 @@
from collections.abc import Generator
from contextlib import contextmanager
from types import MappingProxyType
from unittest.mock import AsyncMock, MagicMock, patch
from unifi_discovery import AIOUnifiScanner, UnifiDevice, UnifiService
@@ -30,6 +31,21 @@ UNIFI_DISCOVERY_NO_MAC = UnifiDevice(
direct_connect_domain=DIRECT_CONNECT_DOMAIN,
)
# In production the crash happens because asdict() deep-copies Enum keys in
# the services dict, and on Python 3.14+ deepcopy traverses into
# Enum.__members__ (a mappingproxy) which cannot be pickled. Whether
# deepcopy(enum) actually fails depends on the specific CPython build, so
# we force the error reliably by using MappingProxyType directly as the
# services value — deepcopy always fails on it.
UNIFI_DISCOVERY_MAPPINGPROXY_SERVICES = UnifiDevice(
source_ip=DEVICE_IP_ADDRESS,
hw_addr=DEVICE_MAC_ADDRESS,
platform=DEVICE_HOSTNAME,
hostname=DEVICE_HOSTNAME,
services=MappingProxyType({UnifiService.Protect: True}),
direct_connect_domain=DIRECT_CONNECT_DOMAIN,
)
def _patch_discovery(
device: UnifiDevice | None = None, no_device: bool = False
+22 -1
View File
@@ -7,7 +7,11 @@ from homeassistant.components.unifi_discovery.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from . import UNIFI_DISCOVERY_NO_MAC, _patch_discovery
from . import (
UNIFI_DISCOVERY_MAPPINGPROXY_SERVICES,
UNIFI_DISCOVERY_NO_MAC,
_patch_discovery,
)
async def test_setup_starts_discovery(hass: HomeAssistant) -> None:
@@ -54,3 +58,20 @@ async def test_dependency_loads_discovery(
flows = hass.config_entries.flow.async_progress_by_handler("unifiprotect")
assert len(flows) == 1
assert flows[0]["context"]["source"] == config_entries.SOURCE_INTEGRATION_DISCOVERY
async def test_discovery_does_not_deepcopy_device(hass: HomeAssistant) -> None:
"""Test discovery works without deepcopy.
In production asdict() deep-copies Enum keys in the services dict which
can crash on Python 3.14+ because Enum.__members__ is a mappingproxy that
cannot be pickled. We force the crash reliably by using MappingProxyType
as the services value.
"""
with _patch_discovery(device=UNIFI_DISCOVERY_MAPPINGPROXY_SERVICES):
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done(wait_background_tasks=True)
flows = hass.config_entries.flow.async_progress_by_handler("unifiprotect")
assert len(flows) == 1
assert flows[0]["context"]["source"] == config_entries.SOURCE_INTEGRATION_DISCOVERY