Import and create pyudev for usb in the executor (#113478)

This commit is contained in:
J. Nick Koston 2024-03-15 09:05:18 -10:00 committed by GitHub
parent 29f07260f9
commit b7f7bed46c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 28 additions and 11 deletions

View file

@ -35,7 +35,7 @@ from .models import USBDevice
from .utils import usb_device_from_port from .utils import usb_device_from_port
if TYPE_CHECKING: if TYPE_CHECKING:
from pyudev import Device from pyudev import Device, MonitorObserver
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -228,6 +228,25 @@ class USBDiscovery:
if info.get("docker"): if info.get("docker"):
return return
if not (
observer := await self.hass.async_add_executor_job(
self._get_monitor_observer
)
):
return
def _stop_observer(event: Event) -> None:
observer.stop()
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _stop_observer)
self.observer_active = True
def _get_monitor_observer(self) -> MonitorObserver | None:
"""Get the monitor observer.
This runs in the executor because the import
does blocking I/O.
"""
from pyudev import ( # pylint: disable=import-outside-toplevel from pyudev import ( # pylint: disable=import-outside-toplevel
Context, Context,
Monitor, Monitor,
@ -237,7 +256,7 @@ class USBDiscovery:
try: try:
context = Context() context = Context()
except (ImportError, OSError): except (ImportError, OSError):
return return None
monitor = Monitor.from_netlink(context) monitor = Monitor.from_netlink(context)
try: try:
@ -246,17 +265,14 @@ class USBDiscovery:
_LOGGER.debug( _LOGGER.debug(
"Unable to setup pyudev filtering; This is expected on WSL: %s", ex "Unable to setup pyudev filtering; This is expected on WSL: %s", ex
) )
return return None
observer = MonitorObserver( observer = MonitorObserver(
monitor, callback=self._device_discovered, name="usb-observer" monitor, callback=self._device_discovered, name="usb-observer"
) )
observer.start() observer.start()
return observer
def _stop_observer(event: Event) -> None:
observer.stop()
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _stop_observer)
self.observer_active = True
def _device_discovered(self, device: Device) -> None: def _device_discovered(self, device: Device) -> None:
"""Call when the observer discovers a new usb tty device.""" """Call when the observer discovers a new usb tty device."""

View file

@ -590,6 +590,7 @@ async def test_supervisor_issues_initial_failure(
with patch("homeassistant.components.hassio.issues.REQUEST_REFRESH_DELAY", new=0.1): with patch("homeassistant.components.hassio.issues.REQUEST_REFRESH_DELAY", new=0.1):
result = await async_setup_component(hass, "hassio", {}) result = await async_setup_component(hass, "hassio", {})
await hass.async_block_till_done()
assert result assert result
client = await hass_ws_client(hass) client = await hass_ws_client(hass)

View file

@ -85,7 +85,7 @@ async def test_observer_discovery(
def _create_mock_monitor_observer(monitor, callback, name): def _create_mock_monitor_observer(monitor, callback, name):
nonlocal mock_observer nonlocal mock_observer
hass.async_create_task(_mock_monitor_observer_callback(callback)) hass.create_task(_mock_monitor_observer_callback(callback))
mock_observer = MagicMock() mock_observer = MagicMock()
return mock_observer return mock_observer
@ -107,7 +107,7 @@ async def test_observer_discovery(
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done() await hass.async_block_till_done()
assert mock_observer.mock_calls == [call.start(), call.stop()] assert mock_observer.mock_calls == [call.start(), call.__bool__(), call.stop()]
@pytest.mark.skipif( @pytest.mark.skipif(