Skip to content

Commit 0854912

Browse files
authored
Drop @callback and other unused async helpers (#75)
* Bye bye `@callback` * Fix unit tests * Drop `async_run_job` * Drop `run_callback_threadsafe`
1 parent 2008457 commit 0854912

File tree

10 files changed

+52
-363
lines changed

10 files changed

+52
-363
lines changed

tests/test_async_.py

Lines changed: 22 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,28 @@
33
import asyncio
44
import functools
55
import time
6-
from unittest.mock import MagicMock, Mock, patch
6+
from unittest.mock import MagicMock, patch
77

88
import pytest
99

1010
from zha import async_ as zha_async
1111
from zha.application.gateway import Gateway
1212
from zha.async_ import AsyncUtilMixin, ZHAJob, ZHAJobType, create_eager_task
13-
from zha.decorators import callback
14-
15-
16-
async def test_zhajob_forbid_coroutine() -> None:
17-
"""Test zhajob forbids coroutines."""
18-
19-
async def bla():
20-
pass
21-
22-
coro = bla()
23-
24-
with pytest.raises(ValueError):
25-
_ = ZHAJob(coro).job_type
26-
27-
# To avoid warning about unawaited coro
28-
await coro
2913

3014

3115
@pytest.mark.parametrize("eager_start", [True, False])
3216
async def test_cancellable_zhajob(zha_gateway: Gateway, eager_start: bool) -> None:
3317
"""Simulate a shutdown, ensure cancellable jobs are cancelled."""
3418
job = MagicMock()
3519

36-
@callback
3720
def run_job(job: ZHAJob) -> None:
3821
"""Call the action."""
3922
zha_gateway.async_run_zha_job(job, eager_start=eager_start)
4023

4124
timer1 = zha_gateway.loop.call_later(
42-
60, run_job, ZHAJob(callback(job), cancel_on_shutdown=True)
25+
60, run_job, ZHAJob(job, cancel_on_shutdown=True)
4326
)
44-
timer2 = zha_gateway.loop.call_later(60, run_job, ZHAJob(callback(job)))
27+
timer2 = zha_gateway.loop.call_later(60, run_job, ZHAJob(job))
4528

4629
await zha_gateway.shutdown()
4730

@@ -57,7 +40,7 @@ async def test_async_add_zha_job_schedule_callback() -> None:
5740
zha_gateway = MagicMock(loop=MagicMock(wraps=asyncio.get_running_loop()))
5841
job = MagicMock()
5942

60-
AsyncUtilMixin.async_add_zha_job(zha_gateway, ZHAJob(callback(job)))
43+
AsyncUtilMixin.async_add_zha_job(zha_gateway, ZHAJob(job))
6144
assert len(zha_gateway.loop.call_soon.mock_calls) == 1
6245
assert len(zha_gateway.loop.create_task.mock_calls) == 0
6346
assert len(zha_gateway.add_job.mock_calls) == 0
@@ -71,9 +54,7 @@ async def test_async_add_zha_job_eager_start_coro_suspends(
7154
async def job_that_suspends():
7255
await asyncio.sleep(0)
7356

74-
task = zha_gateway.async_add_zha_job(
75-
ZHAJob(callback(job_that_suspends)), eager_start=True
76-
)
57+
task = zha_gateway.async_add_zha_job(ZHAJob(job_that_suspends), eager_start=True)
7758
assert not task.done()
7859
assert task in zha_gateway._tracked_completable_tasks
7960
await task
@@ -88,7 +69,7 @@ async def test_async_run_zha_job_eager_start_coro_suspends(
8869
async def job_that_suspends():
8970
await asyncio.sleep(0)
9071

91-
task = zha_gateway.async_run_zha_job(ZHAJob(callback(job_that_suspends)))
72+
task = zha_gateway.async_run_zha_job(ZHAJob(job_that_suspends))
9273
assert not task.done()
9374
assert task in zha_gateway._tracked_completable_tasks
9475
await task
@@ -101,9 +82,7 @@ async def test_async_add_zha_job_background(zha_gateway: Gateway) -> None:
10182
async def job_that_suspends():
10283
await asyncio.sleep(0)
10384

104-
task = zha_gateway.async_add_zha_job(
105-
ZHAJob(callback(job_that_suspends)), background=True
106-
)
85+
task = zha_gateway.async_add_zha_job(ZHAJob(job_that_suspends), background=True)
10786
assert not task.done()
10887
assert task in zha_gateway._background_tasks
10988
await task
@@ -116,9 +95,7 @@ async def test_async_run_zha_job_background(zha_gateway: Gateway) -> None:
11695
async def job_that_suspends():
11796
await asyncio.sleep(0)
11897

119-
task = zha_gateway.async_run_zha_job(
120-
ZHAJob(callback(job_that_suspends)), background=True
121-
)
98+
task = zha_gateway.async_run_zha_job(ZHAJob(job_that_suspends), background=True)
12299
assert not task.done()
123100
assert task in zha_gateway._background_tasks
124101
await task
@@ -131,9 +108,7 @@ async def test_async_add_zha_job_eager_background(zha_gateway: Gateway) -> None:
131108
async def job_that_suspends():
132109
await asyncio.sleep(0)
133110

134-
task = zha_gateway.async_add_zha_job(
135-
ZHAJob(callback(job_that_suspends)), background=True
136-
)
111+
task = zha_gateway.async_add_zha_job(ZHAJob(job_that_suspends), background=True)
137112
assert not task.done()
138113
assert task in zha_gateway._background_tasks
139114
await task
@@ -146,43 +121,28 @@ async def test_async_run_zha_job_eager_background(zha_gateway: Gateway) -> None:
146121
async def job_that_suspends():
147122
await asyncio.sleep(0)
148123

149-
task = zha_gateway.async_run_zha_job(
150-
ZHAJob(callback(job_that_suspends)), background=True
151-
)
124+
task = zha_gateway.async_run_zha_job(ZHAJob(job_that_suspends), background=True)
152125
assert not task.done()
153126
assert task in zha_gateway._background_tasks
154127
await task
155128
assert task not in zha_gateway._background_tasks
156129

157130

158-
async def test_async_run_zha_job_background_synchronous(
131+
@pytest.mark.parametrize("background", [True, False])
132+
async def test_async_run_zha_job_background_no_suspend(
159133
zha_gateway: Gateway,
134+
background: bool,
160135
) -> None:
161136
"""Test scheduling a coro as an eager background task with async_run_zha_job."""
162137

163138
async def job_that_does_not_suspends():
164139
pass
165140

166141
task = zha_gateway.async_run_zha_job(
167-
ZHAJob(callback(job_that_does_not_suspends)),
168-
background=True,
169-
)
170-
assert task.done()
171-
assert task not in zha_gateway._background_tasks
172-
assert task not in zha_gateway._tracked_completable_tasks
173-
await task
174-
175-
176-
async def test_async_run_zha_job_synchronous(zha_gateway: Gateway) -> None:
177-
"""Test scheduling a coro as an eager task with async_run_zha_job."""
178-
179-
async def job_that_does_not_suspends():
180-
pass
181-
182-
task = zha_gateway.async_run_zha_job(
183-
ZHAJob(callback(job_that_does_not_suspends)),
184-
background=False,
142+
ZHAJob(job_that_does_not_suspends),
143+
background=background,
185144
)
145+
assert task is not None
186146
assert task.done()
187147
assert task not in zha_gateway._background_tasks
188148
assert task not in zha_gateway._tracked_completable_tasks
@@ -219,7 +179,7 @@ async def test_async_add_zha_job_schedule_partial_callback() -> None:
219179
"""Test that we schedule partial coros and add jobs to the job pool."""
220180
zha_gateway = MagicMock(loop=MagicMock(wraps=asyncio.get_running_loop()))
221181
job = MagicMock()
222-
partial = functools.partial(callback(job))
182+
partial = functools.partial(job)
223183

224184
AsyncUtilMixin.async_add_zha_job(zha_gateway, ZHAJob(partial))
225185
assert len(zha_gateway.loop.call_soon.mock_calls) == 1
@@ -252,6 +212,7 @@ async def job():
252212
) as mock_create_eager_task:
253213
zha_job = ZHAJob(job)
254214
task = AsyncUtilMixin.async_add_zha_job(zha_gateway, zha_job, eager_start=True)
215+
assert task is not None
255216
assert len(zha_gateway.loop.call_soon.mock_calls) == 0
256217
assert len(zha_gateway.add_job.mock_calls) == 0
257218
assert mock_create_eager_task.mock_calls
@@ -283,9 +244,9 @@ def job():
283244
pass
284245

285246
AsyncUtilMixin.async_add_zha_job(zha_gateway, ZHAJob(job))
286-
assert len(zha_gateway.loop.call_soon.mock_calls) == 0
247+
assert len(zha_gateway.loop.call_soon.mock_calls) == 1
287248
assert len(zha_gateway.loop.create_task.mock_calls) == 0
288-
assert len(zha_gateway.loop.run_in_executor.mock_calls) == 2
249+
assert len(zha_gateway.loop.run_in_executor.mock_calls) == 0
289250

290251

291252
async def test_async_create_task_schedule_coroutine() -> None:
@@ -337,7 +298,7 @@ def job():
337298
asyncio.get_running_loop() # ensure we are in the event loop
338299
calls.append(1)
339300

340-
AsyncUtilMixin.async_run_zha_job(zha_gateway, ZHAJob(callback(job)))
301+
AsyncUtilMixin.async_run_zha_job(zha_gateway, ZHAJob(job))
341302
assert len(calls) == 1
342303

343304

@@ -360,24 +321,11 @@ async def test_async_run_zha_job_calls_callback() -> None:
360321
def job():
361322
calls.append(1)
362323

363-
AsyncUtilMixin.async_run_zha_job(zha_gateway, ZHAJob(callback(job)))
324+
AsyncUtilMixin.async_run_zha_job(zha_gateway, ZHAJob(job))
364325
assert len(calls) == 1
365326
assert len(zha_gateway.async_add_job.mock_calls) == 0
366327

367328

368-
async def test_async_run_zha_job_delegates_non_async() -> None:
369-
"""Test that the callback annotation is respected."""
370-
zha_gateway = MagicMock()
371-
calls = []
372-
373-
def job():
374-
calls.append(1)
375-
376-
AsyncUtilMixin.async_run_zha_job(zha_gateway, ZHAJob(job))
377-
assert len(calls) == 0
378-
assert len(zha_gateway.async_add_zha_job.mock_calls) == 1
379-
380-
381329
async def test_async_create_task_pending_tasks_coro(zha_gateway: Gateway) -> None:
382330
"""Add a coro to pending tasks."""
383331
call_count = []
@@ -395,34 +343,6 @@ async def test_coro():
395343
assert len(zha_gateway._tracked_completable_tasks) == 0
396344

397345

398-
async def test_async_run_job_starts_tasks_eagerly(zha_gateway: Gateway) -> None:
399-
"""Test async_run_job starts tasks eagerly."""
400-
runs = []
401-
402-
async def _test():
403-
runs.append(True)
404-
405-
task = zha_gateway.async_run_job(_test)
406-
# No call to zha_gateway.async_block_till_done to ensure the task is run eagerly
407-
assert len(runs) == 1
408-
assert task.done()
409-
await task
410-
411-
412-
async def test_async_run_job_starts_coro_eagerly(zha_gateway: Gateway) -> None:
413-
"""Test async_run_job starts coros eagerly."""
414-
runs = []
415-
416-
async def _test():
417-
runs.append(True)
418-
419-
task = zha_gateway.async_run_job(_test())
420-
# No call to zha_gateway.async_block_till_done to ensure the task is run eagerly
421-
assert len(runs) == 1
422-
assert task.done()
423-
await task
424-
425-
426346
@pytest.mark.parametrize("eager_start", [True, False])
427347
async def test_background_task(zha_gateway: Gateway, eager_start: bool) -> None:
428348
"""Test background tasks being quit."""
@@ -447,7 +367,6 @@ async def test_task():
447367
def test_ZHAJob_passing_job_type():
448368
"""Test passing the job type to ZHAJob when we already know it."""
449369

450-
@callback
451370
def callback_func():
452371
pass
453372

@@ -506,31 +425,6 @@ async def _async_add_executor_job():
506425
await task
507426

508427

509-
@patch("concurrent.futures.Future")
510-
@patch("threading.get_ident")
511-
def test_run_callback_threadsafe_from_inside_event_loop(mock_ident, _) -> None:
512-
"""Testing calling run_callback_threadsafe from inside an event loop."""
513-
callback_fn = MagicMock()
514-
515-
loop = Mock(spec=["call_soon_threadsafe"])
516-
517-
loop._thread_ident = None
518-
mock_ident.return_value = 5
519-
zha_async.run_callback_threadsafe(loop, callback_fn)
520-
assert len(loop.call_soon_threadsafe.mock_calls) == 1
521-
522-
loop._thread_ident = 5
523-
mock_ident.return_value = 5
524-
with pytest.raises(RuntimeError):
525-
zha_async.run_callback_threadsafe(loop, callback_fn)
526-
assert len(loop.call_soon_threadsafe.mock_calls) == 1
527-
528-
loop._thread_ident = 1
529-
mock_ident.return_value = 5
530-
zha_async.run_callback_threadsafe(loop, callback_fn)
531-
assert len(loop.call_soon_threadsafe.mock_calls) == 2
532-
533-
534428
async def test_gather_with_limited_concurrency() -> None:
535429
"""Test gather_with_limited_concurrency limits the number of running tasks."""
536430

@@ -553,74 +447,6 @@ async def _increment_runs_if_in_time():
553447
assert results == [2, 2, -1, -1]
554448

555449

556-
async def test_shutdown_run_callback_threadsafe(zha_gateway: Gateway) -> None:
557-
"""Test we can shutdown run_callback_threadsafe."""
558-
zha_async.shutdown_run_callback_threadsafe(zha_gateway.loop)
559-
callback_fn = MagicMock()
560-
561-
with pytest.raises(RuntimeError):
562-
zha_async.run_callback_threadsafe(zha_gateway.loop, callback_fn)
563-
564-
565-
async def test_run_callback_threadsafe(zha_gateway: Gateway) -> None:
566-
"""Test run_callback_threadsafe runs code in the event loop."""
567-
it_ran = False
568-
569-
def callback_fn():
570-
nonlocal it_ran
571-
it_ran = True
572-
573-
assert zha_async.run_callback_threadsafe(zha_gateway.loop, callback_fn)
574-
assert it_ran is False
575-
576-
# Verify that async_block_till_done will flush
577-
# out the callback
578-
await zha_gateway.async_block_till_done()
579-
assert it_ran is True
580-
581-
582-
async def test_run_callback_threadsafe_exception(zha_gateway: Gateway) -> None:
583-
"""Test run_callback_threadsafe runs code in the event loop."""
584-
it_ran = False
585-
586-
def callback_fn():
587-
nonlocal it_ran
588-
it_ran = True
589-
raise ValueError("Test")
590-
591-
future = zha_async.run_callback_threadsafe(zha_gateway.loop, callback_fn)
592-
assert future
593-
assert it_ran is False
594-
595-
# Verify that async_block_till_done will flush
596-
# out the callback
597-
await zha_gateway.async_block_till_done()
598-
assert it_ran is True
599-
600-
with pytest.raises(ValueError):
601-
future.result()
602-
603-
604-
async def test_callback_is_always_scheduled(zha_gateway: Gateway) -> None:
605-
"""Test run_callback_threadsafe always calls call_soon_threadsafe before checking for shutdown."""
606-
# We have to check the shutdown state AFTER the callback is scheduled otherwise
607-
# the function could continue on and the caller call `future.result()` after
608-
# the point in the main thread where callbacks are no longer run.
609-
610-
callback_fn = MagicMock()
611-
zha_async.shutdown_run_callback_threadsafe(zha_gateway.loop)
612-
613-
with (
614-
patch.object(
615-
zha_gateway.loop, "call_soon_threadsafe"
616-
) as mock_call_soon_threadsafe,
617-
pytest.raises(RuntimeError),
618-
):
619-
zha_async.run_callback_threadsafe(zha_gateway.loop, callback_fn)
620-
621-
mock_call_soon_threadsafe.assert_called_once()
622-
623-
624450
async def test_create_eager_task_312(zha_gateway: Gateway) -> None: # pylint: disable=unused-argument
625451
"""Test create_eager_task schedules a task eagerly in the event loop.
626452

0 commit comments

Comments
 (0)