#I don't get this comment: <https://

1 messages ยท Page 1 of 1 (latest)

lament schooner
#

and you can't alter/mock local variables, which was the issue Erwin was trying to get across

keen knoll
#

right but can't you add something to hass._tasks before async_stop gets called?

lament schooner
#

i've tried that, actually

async def test_stage_shutdown_generic_error(hass: HomeAssistant, caplog) -> None:
    """Simulate a shutdown, test that a generic error at the final stage doesn't prevent it."""

    never_set = asyncio.Event()

    async def dummy_task() -> None:
        await never_set.wait()
        return

    def wait_and_fail():
        time.sleep(0.05)
        never_set.set()
        raise Exception("final stage task cancellation error")

    task = hass.loop.create_task(dummy_task(), name="dummy_task")
    with patch.object(task, "cancel", side_effect=wait_and_fail()):
        await hass.async_stop()

    assert "final stage task cancellation error" in caplog.text
    assert hass.state == CoreState.stopped
#

i seem to be doing something wrong

FAILED tests/test_core.py::test_stage_shutdown_generic_error - RuntimeError: Detected blocking call to sleep inside the event loop. Use `await hass.async_add_executor_job()`; This is causing stability issues. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue
FAILED tests/test_core.py::test_stage_shutdown_generic_error - Failed: Lingering task after test <Task pending name='dummy_task' coro=<test_stage_shutdown_generic_error.<locals>.dummy_task() running at /workspaces/ha-core/tests/test_core.py:421> wait_for=<Future pending cb=[Task.task_wakeup()] created a...
keen knoll
#

instead of setting the event

#

just raise the exception after sleeping

lament schooner
#

yeah, that didn't work either

#

let me try again

#
AILED tests/test_core.py::test_stage_shutdown_generic_error - RuntimeError: Detected blocking call to sleep inside the event loop. Use `await hass.async_add_executor_job()`; This is causing stability issues. Please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue
FAILED tests/test_core.py::test_stage_shutdown_generic_error - Failed: Lingering task after test <Task pending name='dummy_task' coro=<test_stage_shutdown_generic_error.<locals>.dummy_task() running at /workspaces/ha-core/tests/test_core.py:421> wait_for=<Future pending cb=[Task.task_wakeup()] created a...
pliant nova
#

Great to see more people are helping!

lament schooner
#

Raman always did

keen knoll
#

yeah ok so the exception is being set in the wrong place

lament schooner
#

i just realized that i don't think wait_and_fail should be called when passed as the side_effect, since it's not async

#

so i've removed the calling parentheses () and it's a bit different now

FAILED tests/test_core.py::test_stage_shutdown_generic_error - AssertionError: assert 'final stage task cancellation error' in '2023-11-17 14:22:08.890 DEBUG    MainThread homeassistant.core:core.py:1106 Bus:Handling <Event homeassistant_stoppin...3-11-17 14:22:08.892 DEBUG    MainThread homeassistant.c...
FAILED tests/test_core.py::test_stage_shutdown_generic_error - Failed: Lingering task after test <Task pending name='dummy_task' coro=<test_stage_shutdown_generic_error.<locals>.dummy_task() running at /workspaces/ha-core/tests/test_core.py:421> wait_for=<Future pending cb=[Task.task_wakeup()] created a... 
#

that doesn't mean I am able to properly call it

keen knoll
#

so here's what I am playing with

#
async def test_stage_shutdown_generic_error(hass: HomeAssistant, caplog) -> None:
    """Simulate a shutdown, test that a generic error at the final stage doesn't prevent it."""

    never_set = asyncio.Event()

    async def dummy_task() -> None:
        await never_set.wait()
        return

    task = hass.loop.create_task(dummy_task(), name="dummy_task")

    def wait_and_fail():
        time.sleep(0.05)
        task.set_exception(Exception("final stage task cancellation error"))

    with patch.object(task, "cancel", side_effect=wait_and_fail):
        await hass.async_stop()

    assert "final stage task cancellation error" in caplog.text
    assert hass.state == ha.CoreState.stopped
#

instead of raising an exception in the side effect, I am trying to set the exception on the task

#

this test should work even without your PR right?

lament schooner
#

i think so, yes. old code was revealed as not covered

#

i see what you're doing, but doesn't set_exception imply that the task is run instead of cancelled?

keen knoll
#

good question

lament schooner
#

as far as I can tell, that code above never runs wait_and_fail. i don't know why

#

neither does mine, actually

#

yup...

    with patch.object(task, "cancel", side_effect=wait_and_fail) as patched_cancel:
        await hass.async_stop()
        assert patched_cancel.called
FAILED tests/test_core.py::test_stage_shutdown_generic_error - AssertionError: assert False
keen knoll
#

right well you aren't even seeing this warning:

_LOGGER.warning(
                "Task %s was still running after stage 2 shutdown; "
                "Integrations should cancel non-critical tasks when receiving "
                "the stop event to prevent delaying shutdown",
                task,
            )
lament schooner
#

hm. true

#

i re-worded that warning due to stages being renamed, but i understand there should have been at least a warning

keen knoll
#

are you seeing the lingering task error?

lament schooner
#

yes

#

that's because i never set the event

#

if I set it after await hass.async_stop() the warning goes away

keen knoll
#

the task never gets added to hass's task list

#

here's what running_tasks looks like before you loop through to cancel: 2023-11-17 09:44:10.079 ERROR MainThread homeassistant.core:core.py:851 set()

keen knoll
#

no

lament schooner
#

oh, wait, you said running_tasks. ๐Ÿคจ i don't understand

keen knoll
#

it's because you are creating the task directly on the loop

lament schooner
#

so what's the way to do it, then? hass.async_add_something?

keen knoll
#

you need to call hass.async_create_task

#

it's just a wrapper that adds it to the task list as well

#

here's where I am at so far

#
async def test_stage_shutdown_generic_error(hass: HomeAssistant, caplog) -> None:
    """Simulate a shutdown, test that a generic error at the final stage doesn't prevent it."""

    never_set = asyncio.Event()

    async def dummy_task() -> None:
        await never_set.wait()
        return

    task = hass.async_create_task(dummy_task(), name="dummy_task")

    async def wait_and_fail():
        await asyncio.sleep(0.05)
        return Exception("test_exception")

    with patch.object(task, "_make_cancelled_error", side_effect=wait_and_fail):
        await hass.async_stop()

    assert "test_exception" in caplog.text
    assert hass.state == ha.CoreState.stopped
lament schooner
#

right, that's what i just edited. good catch!

#

you have a return Exception. should that be raise Exception?

keen knoll
#

well, I was trying to go deeper into the task

#

_make_cancelled_error returns the exception, but I don't think it gets called in this case

lament schooner
keen knoll
#

yeah it only gets called when you try to access the result of the task and it's been cancelled

#

went down the wrong path

#

try this

#
async def test_stage_shutdown_generic_error(hass: HomeAssistant, caplog) -> None:
    """Simulate a shutdown, test that a generic error at the final stage doesn't prevent it."""
    task = asyncio.Future()
    hass._tasks.add(task)

    def wait_and_fail(_):
        task.set_exception(Exception("test_exception"))

    with patch.object(task, "cancel", side_effect=wait_and_fail) as patched_call:
        await hass.async_stop()

    assert "test_exception" in caplog.text
    assert hass.state == ha.CoreState.stopped
#

the issue was that you needed to set the exception directly on the task, raising an exception during the cancel function wouldn't accomplish anything because the task itself didn't have the exception

#

but asyncio.Task doesn't let you set an exception on it externally I geuss? asyncio.Future does

pliant nova
lament schooner
#

let's see what the code coverage test says

pliant nova
lament schooner
#

this is super weird. indeed it says that the branch with generic exceptions is covered, but for some reason, it goes back to saying that the other ones aren't ๐Ÿ™‚

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html

---------- coverage: platform linux, python 3.11.4-final-0 -----------
Name                    Stmts   Miss  Cover   Missing
-----------------------------------------------------
homeassistant/core.py     919     72    92%   395-403, 412-426, 462-466, 484, 715, 727, 775-779, 798-799, 802, 873, 885, 888, 916, 934, 1068, 1078, 1120-1122, 1126-1127, 1141-1149, 1176, 1211-1219, 1357, 1387, 1485, 1530-1533, 1574, 1617, 1660, 1746, 1850, 1874-1876, 1931, 1971, 2039, 2044-2051, 2095, 2101, 2188, 2303-2304, 2449-2450
-----------------------------------------------------
TOTAL                     919     72    92%
#

873, 885, 888 in particular

#

although... that may have been expected, actually ๐Ÿค”

keen knoll
#

yeah you don't change or cover those

lament schooner
#

yes, exactly

keen knoll
#

your changes are in lines 400-460

#

so I think you are OK from a patch perspective

lament schooner
#

lemme push and see

#

thanks loads, Raman!

keen knoll
#

sure, fun challenge ๐Ÿ™‚

lament schooner
lament schooner
#

Oh, so am i