No need for a local import of the paho mqtt client (#169925)

This commit is contained in:
Jan Bouwhuis
2026-05-06 22:45:36 +02:00
committed by GitHub
parent c92128b282
commit 046298f2ca
8 changed files with 19 additions and 68 deletions
+3 -23
View File
@@ -16,6 +16,8 @@ from typing import TYPE_CHECKING, Any
from uuid import uuid4
import certifi
import paho.mqtt.client as mqtt
from paho.mqtt.matcher import MQTTMatcher
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@@ -47,6 +49,7 @@ from homeassistant.setup import SetupPhases, async_pause_setup
from homeassistant.util.collection import chunked_or_all
from homeassistant.util.logging import catch_log_exception, log_exception
from .async_client import AsyncMQTTClient
from .const import (
CONF_BIRTH_MESSAGE,
CONF_BROKER,
@@ -86,13 +89,6 @@ from .models import (
)
from .util import EnsureJobAfterCooldown, get_file_path, mqtt_config_entry_enabled
if TYPE_CHECKING:
# Only import for paho-mqtt type checking here, imports are done locally
# because integrations should be able to optionally rely on MQTT.
import paho.mqtt.client as mqtt
from .async_client import AsyncMQTTClient
_LOGGER = logging.getLogger(__name__)
MIN_BUFFER_SIZE = 131072 # Minimum buffer size to use if preferred size fails
@@ -323,12 +319,6 @@ class MqttClientSetup:
The setup of the MQTT client should be run in an executor job,
because it accesses files, so it does IO.
"""
# We don't import on the top because some integrations
# should be able to optionally rely on MQTT.
from paho.mqtt import client as mqtt # noqa: PLC0415
from .async_client import AsyncMQTTClient # noqa: PLC0415
config = self._config
clean_session: bool | None = None
# If no protocol setting is set in the config entry data
@@ -561,7 +551,6 @@ class MQTT:
"""Start the misc periodic."""
assert self._misc_timer is None, "Misc periodic already started"
_LOGGER.debug("%s: Starting client misc loop", self.config_entry.title)
import paho.mqtt.client as mqtt # noqa: PLC0415
# Inner function to avoid having to check late import
# each time the function is called.
@@ -705,7 +694,6 @@ class MQTT:
async def async_connect(self, client_available: asyncio.Future[bool]) -> None:
"""Connect to the host. Does not process messages yet."""
import paho.mqtt.client as mqtt # noqa: PLC0415
result: int | None = None
self._available_future = client_available
@@ -763,7 +751,6 @@ class MQTT:
async def _reconnect_loop(self) -> None:
"""Reconnect to the MQTT server."""
import paho.mqtt.client as mqtt # noqa: PLC0415
while True:
if not self.connected:
@@ -1265,9 +1252,6 @@ class MQTT:
@callback
def _async_handle_callback_exception(self, status: mqtt.MQTTErrorCode) -> None:
"""Handle a callback exception."""
# We don't import on the top because some integrations
# should be able to optionally rely on MQTT.
import paho.mqtt.client as mqtt # noqa: PLC0415
_LOGGER.warning(
"Error returned from MQTT server: %s",
@@ -1312,8 +1296,6 @@ class MQTT:
) -> None:
"""Wait for ACK from broker or raise on error."""
if result_code != 0:
import paho.mqtt.client as mqtt # noqa: PLC0415
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="mqtt_broker_error",
@@ -1360,8 +1342,6 @@ class MQTT:
def _matcher_for_topic(subscription: str) -> Callable[[str], bool]:
from paho.mqtt.matcher import MQTTMatcher # noqa: PLC0415
matcher = MQTTMatcher() # type: ignore[no-untyped-call]
matcher[subscription] = True
+1 -4
View File
@@ -22,6 +22,7 @@ from cryptography.hazmat.primitives.serialization import (
load_pem_private_key,
)
from cryptography.x509 import load_der_x509_certificate, load_pem_x509_certificate
import paho.mqtt.client as mqtt
import voluptuous as vol
import yaml
@@ -5479,10 +5480,6 @@ def try_connection(
user_input: dict[str, Any],
) -> bool:
"""Test if we can connect to an MQTT broker."""
# We don't import on the top because some integrations
# should be able to optionally rely on MQTT.
import paho.mqtt.client as mqtt # noqa: PLC0415
mqtt_client_setup = MqttClientSetup(user_input)
mqtt_client_setup.setup()
client = mqtt_client_setup.client
+2 -2
View File
@@ -9,6 +9,8 @@ from enum import StrEnum
import logging
from typing import TYPE_CHECKING, Any, TypedDict
from paho.mqtt.client import MQTTMessage
from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME, Platform
from homeassistant.core import CALLBACK_TYPE, callback
from homeassistant.exceptions import ServiceValidationError, TemplateError
@@ -24,8 +26,6 @@ from homeassistant.helpers.typing import (
from homeassistant.util.hass_dict import HassKey
if TYPE_CHECKING:
from paho.mqtt.client import MQTTMessage
from .client import MQTT, Subscription
from .debug_info import TimestampedPublishMessage
from .device_trigger import Trigger
+1 -5
View File
@@ -29,6 +29,7 @@ from unittest.mock import AsyncMock, Mock, patch
from aiohttp.test_utils import unused_port as get_test_instance_port
from annotatedyaml import load_yaml_dict, loader as yaml_loader
import attr
from paho.mqtt.client import MQTTMessage
import pytest
from syrupy.assertion import SnapshotAssertion
import voluptuous as vol
@@ -453,11 +454,6 @@ def async_fire_mqtt_message(
retain: bool = False,
) -> None:
"""Fire the MQTT message."""
# Local import to avoid processing MQTT modules when running a testcase
# which does not use MQTT.
from paho.mqtt.client import MQTTMessage # noqa: PLC0415
from homeassistant.components.mqtt import MqttData # noqa: PLC0415
if isinstance(payload, str):
+7 -19
View File
@@ -88,9 +88,7 @@ async def test_mqtt_await_ack_at_disconnect(hass: HomeAssistant) -> None:
mid = 100
rc = 0
with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client:
with patch("homeassistant.components.mqtt.client.AsyncMQTTClient") as mock_client:
mqtt_client = mock_client.return_value
mqtt_client.connect = MagicMock(
return_value=0,
@@ -1305,9 +1303,7 @@ async def test_publish_error(
entry.add_to_hass(hass)
# simulate an Out of memory error
with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client:
with patch("homeassistant.components.mqtt.client.AsyncMQTTClient") as mock_client:
mock_client().connect = lambda **kwargs: 1
mock_client().publish().rc = 1
assert await hass.config_entries.async_setup(entry.entry_id)
@@ -1404,9 +1400,7 @@ async def test_setup_mqtt_client_clean_session_and_protocol(
clean_session: bool | None,
) -> None:
"""Test MQTT client clean_session and protocol setup."""
with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client:
with patch("homeassistant.components.mqtt.client.AsyncMQTTClient") as mock_client:
await mqtt_mock_entry()
# check if clean_session was correctly
@@ -1470,9 +1464,7 @@ async def test_handle_mqtt_timeout_on_callback(
mid = 102
rc = 0
with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client:
with patch("homeassistant.components.mqtt.client.AsyncMQTTClient") as mock_client:
def _mock_ack(topic: str, qos: int = 0) -> tuple[int, int]:
# Handle ACK for subscribe normally
@@ -1539,9 +1531,7 @@ async def test_setup_raises_config_entry_not_ready_if_no_connect_broker(
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client:
with patch("homeassistant.components.mqtt.client.AsyncMQTTClient") as mock_client:
mock_client().connect = MagicMock(side_effect=exception)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
@@ -1576,9 +1566,7 @@ async def test_setup_uses_certificate_on_certificate_set_to_auto_and_insecure(
def mock_tls_insecure_set(insecure_param) -> None:
insecure_check["insecure"] = insecure_param
with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client:
with patch("homeassistant.components.mqtt.client.AsyncMQTTClient") as mock_client:
mock_client().tls_set = mock_tls_set
mock_client().tls_insecure_set = mock_tls_insecure_set
await mqtt_mock_entry()
@@ -1618,7 +1606,7 @@ async def test_client_id_is_set(
) -> None:
"""Test setup defaults for tls."""
with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
"homeassistant.components.mqtt.client.AsyncMQTTClient"
) as async_client_mock:
await mqtt_mock_entry()
await hass.async_block_till_done()
+2 -6
View File
@@ -254,9 +254,7 @@ def mock_try_connection_success() -> Generator[MqttMockPahoClient]:
mock_client().on_unsubscribe(mock_client, 0, mid, [MockMqttReasonCode()], None)
return (0, mid)
with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client:
with patch("homeassistant.components.mqtt.client.AsyncMQTTClient") as mock_client:
mock_client().loop_start = loop_start
mock_client().subscribe = _subscribe
mock_client().unsubscribe = _unsubscribe
@@ -270,9 +268,7 @@ def mock_try_connection_time_out() -> Generator[MagicMock]:
# Patch prevent waiting 5 sec for a timeout
with (
patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client,
patch("homeassistant.components.mqtt.client.AsyncMQTTClient") as mock_client,
patch("homeassistant.components.mqtt.config_flow.MQTT_TIMEOUT", 0),
):
mock_client().loop_start = lambda *args: 1
+2 -6
View File
@@ -10,6 +10,7 @@ from typing import Any, TypedDict
from unittest.mock import ANY, MagicMock, Mock, mock_open, patch
from freezegun.api import FrozenDateTimeFactory
from paho.mqtt.client import MQTTMessage
import pytest
import voluptuous as vol
@@ -700,11 +701,6 @@ async def test_receiving_message_with_non_utf8_topic_gets_logged(
await mqtt_mock_entry()
await mqtt.async_subscribe(hass, "test-topic", record_calls)
# Local import to avoid processing MQTT modules when running a testcase
# which does not use MQTT.
from paho.mqtt.client import MQTTMessage # noqa: PLC0415
from homeassistant.components.mqtt.models import MqttData # noqa: PLC0415
msg = MQTTMessage(topic=b"tasmota/discovery/18FE34E0B760\xcc\x02")
@@ -1910,7 +1906,7 @@ async def test_link_config_entry(
assert _check_entities() == 2
# reload entry and assert again
with patch("homeassistant.components.mqtt.async_client.AsyncMQTTClient"):
with patch("homeassistant.components.mqtt.client.AsyncMQTTClient"):
await hass.config_entries.async_reload(mqtt_config_entry.entry_id)
await hass.async_block_till_done()
+1 -3
View File
@@ -1057,9 +1057,7 @@ def mqtt_client_mock(hass: HomeAssistant) -> Generator[MqttMockPahoClient]:
self.mid = mid
self.rc = 0
with patch(
"homeassistant.components.mqtt.async_client.AsyncMQTTClient"
) as mock_client:
with patch("homeassistant.components.mqtt.client.AsyncMQTTClient") as mock_client:
# The below use a call_soon for the on_publish/on_subscribe/on_unsubscribe
# callbacks to simulate the behavior of the real MQTT client which will
# not be synchronous.