diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index 59762e5cb92..0e9f7f06e21 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -835,8 +835,13 @@ class MQTT: msg: ReceiveMessage, ) -> str: """Return a string with the exception message.""" + # if msg_callback is a partial we return the name of the first argument + if isinstance(msg_callback, partial): + call_back_name = getattr(msg_callback.args[0], "__name__") # type: ignore[unreachable] + else: + call_back_name = getattr(msg_callback, "__name__") return ( - f"Exception in {msg_callback.__name__} when handling msg on " + f"Exception in {call_back_name} when handling msg on " f"'{msg.topic}': '{msg.payload}'" # type: ignore[str-bytes-safe] ) diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 08f1d8ca099..57056819784 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -4,6 +4,7 @@ import asyncio from collections.abc import Generator from copy import deepcopy from datetime import datetime, timedelta +from functools import partial import json import logging import socket @@ -2912,8 +2913,8 @@ async def test_message_callback_exception_gets_logged( await mqtt_mock_entry() @callback - def bad_handler(*args) -> None: - """Record calls.""" + def bad_handler(msg: ReceiveMessage) -> None: + """Handle callback.""" raise ValueError("This is a bad message callback") await mqtt.async_subscribe(hass, "test-topic", bad_handler) @@ -2926,6 +2927,40 @@ async def test_message_callback_exception_gets_logged( ) +@pytest.mark.no_fail_on_log_exception +async def test_message_partial_callback_exception_gets_logged( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + mqtt_mock_entry: MqttMockHAClientGenerator, +) -> None: + """Test exception raised by message handler.""" + await mqtt_mock_entry() + + @callback + def bad_handler(msg: ReceiveMessage) -> None: + """Handle callback.""" + raise ValueError("This is a bad message callback") + + def parial_handler( + msg_callback: MessageCallbackType, + attributes: set[str], + msg: ReceiveMessage, + ) -> None: + """Partial callback handler.""" + msg_callback(msg) + + await mqtt.async_subscribe( + hass, "test-topic", partial(parial_handler, bad_handler, {"some_attr"}) + ) + async_fire_mqtt_message(hass, "test-topic", "test") + await hass.async_block_till_done() + + assert ( + "Exception in bad_handler when handling msg on 'test-topic':" + " 'test'" in caplog.text + ) + + async def test_mqtt_ws_subscription( hass: HomeAssistant, hass_ws_client: WebSocketGenerator,