Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions homeassistant/helpers/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from homeassistant.core import callback
from homeassistant.loader import bind_hass
from homeassistant.util.async_ import run_callback_threadsafe
from homeassistant.util.logging import catch_log_exception
from .typing import HomeAssistantType


Expand Down Expand Up @@ -40,13 +41,18 @@ def async_dispatcher_connect(hass: HomeAssistantType, signal: str,
if signal not in hass.data[DATA_DISPATCHER]:
hass.data[DATA_DISPATCHER][signal] = []

hass.data[DATA_DISPATCHER][signal].append(target)
wrapped_target = catch_log_exception(
target, lambda *args:
"Exception in {} when dispatching '{}': {}".format(
target.__name__, signal, args))

hass.data[DATA_DISPATCHER][signal].append(wrapped_target)

@callback
def async_remove_dispatcher() -> None:
"""Remove signal listener."""
try:
hass.data[DATA_DISPATCHER][signal].remove(target)
hass.data[DATA_DISPATCHER][signal].remove(wrapped_target)
except (KeyError, ValueError):
# KeyError is key target listener did not exist
# ValueError if listener did not exist within signal
Expand Down
43 changes: 22 additions & 21 deletions tests/components/mqtt/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
from homeassistant.const import (EVENT_CALL_SERVICE, ATTR_DOMAIN, ATTR_SERVICE,
EVENT_HOMEASSISTANT_STOP)

from tests.common import (get_test_home_assistant, mock_coro,
mock_mqtt_component,
threadsafe_coroutine_factory, fire_mqtt_message,
async_fire_mqtt_message, MockConfigEntry)
from tests.common import (
MockConfigEntry, async_fire_mqtt_message, async_mock_mqtt_component,
fire_mqtt_message, get_test_home_assistant, mock_coro, mock_mqtt_component,
threadsafe_coroutine_factory)


@pytest.fixture
Expand Down Expand Up @@ -297,23 +297,6 @@ def test_receiving_non_utf8_message_gets_logged(self):
"b'\\x9a' on test-topic with encoding utf-8" in \
test_handle.output[0]

def test_message_callback_exception_gets_logged(self):
"""Test exception raised by message handler."""
@callback
def bad_handler(*args):
"""Record calls."""
raise Exception('This is a bad message callback')
mqtt.subscribe(self.hass, 'test-topic', bad_handler)

with self.assertLogs(level='WARNING') as test_handle:
fire_mqtt_message(self.hass, 'test-topic', 'test')

self.hass.block_till_done()
assert \
"Exception in bad_handler when handling msg on 'test-topic':" \
" 'test'" in \
test_handle.output[0]

def test_all_subscriptions_run_when_decode_fails(self):
"""Test all other subscriptions still run when decode fails for one."""
mqtt.subscribe(self.hass, 'test-topic', self.record_calls,
Expand Down Expand Up @@ -766,3 +749,21 @@ def test_mqtt_subscribes_topics_on_connect(hass):
async def test_setup_fails_without_config(hass):
"""Test if the MQTT component fails to load with no config."""
assert not await async_setup_component(hass, mqtt.DOMAIN, {})


async def test_message_callback_exception_gets_logged(hass, caplog):
"""Test exception raised by message handler."""
await async_mock_mqtt_component(hass)

@callback
def bad_handler(*args):
"""Record calls."""
raise Exception('This is a bad message callback')

await mqtt.async_subscribe(hass, 'test-topic', bad_handler)
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
19 changes: 18 additions & 1 deletion tests/helpers/test_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from homeassistant.core import callback
from homeassistant.helpers.dispatcher import (
dispatcher_send, dispatcher_connect)
async_dispatcher_connect, dispatcher_send, dispatcher_connect)

from tests.common import get_test_home_assistant

Expand Down Expand Up @@ -134,3 +134,20 @@ def test_funct(data1, data2, data3):
self.hass.block_till_done()

assert calls == [3, 2, 'bla']


async def test_callback_exception_gets_logged(hass, caplog):
"""Test exception raised by signal handler."""
@callback
def bad_handler(*args):
"""Record calls."""
raise Exception('This is a bad message callback')

async_dispatcher_connect(hass, 'test', bad_handler)
dispatcher_send(hass, 'test', 'bad')
await hass.async_block_till_done()
await hass.async_block_till_done()

assert \
"Exception in bad_handler when dispatching 'test': ('bad',)" \
in caplog.text