Skip to content

Commit 0c299e4

Browse files
committed
Copilot + Noah review
1 parent 0b948d8 commit 0c299e4

2 files changed

Lines changed: 84 additions & 148 deletions

File tree

test/asynchronous/test_periodic_executor.py

Lines changed: 42 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -30,38 +30,27 @@
3030
_IS_SYNC = False
3131

3232

33-
def _make_executor(interval=30.0, min_interval=0.01, target=None, name="test"):
34-
if target is None:
33+
class TestAsyncPeriodicExecutor(AsyncUnitTest):
34+
def _make_executor(self, interval=30.0, min_interval=0.01, target=None, name="test"):
35+
if target is None:
3536

36-
async def target():
37-
return True
38-
39-
return AsyncPeriodicExecutor(
40-
interval=interval, min_interval=min_interval, target=target, name=name
41-
)
42-
43-
44-
class AsyncPeriodicExecutorTestBase(AsyncUnitTest):
45-
async def asyncSetUp(self):
46-
self.executor = None
47-
48-
async def asyncTearDown(self):
49-
if self.executor is not None:
50-
self.executor.close()
51-
await self.executor.join(timeout=2)
37+
async def target():
38+
return True
5239

40+
executor = AsyncPeriodicExecutor(
41+
interval=interval, min_interval=min_interval, target=target, name=name
42+
)
43+
self.addAsyncCleanup(self._close_executor, executor)
44+
return executor
5345

54-
class TestAsyncPeriodicExecutor(AsyncPeriodicExecutorTestBase):
55-
async def test_repr_contains_class_and_name(self):
56-
executor = _make_executor(name="exec")
57-
executor_repr = repr(executor)
58-
self.assertIn("AsyncPeriodicExecutor", executor_repr)
59-
self.assertIn("exec", executor_repr)
46+
async def _close_executor(self, executor):
47+
executor.close()
48+
await executor.join(timeout=2)
6049

6150
async def test_join_without_open_is_safe(self):
62-
self.executor = _make_executor()
51+
executor = self._make_executor()
6352
try:
64-
await self.executor.join(timeout=0.01)
53+
await executor.join(timeout=0.01)
6554
except Exception as e:
6655
self.fail(f"join() raised unexpected Exception {e}")
6756

@@ -75,47 +64,25 @@ async def target():
7564
ran.set()
7665
return False
7766

78-
self.executor = _make_executor(target=target)
79-
self.executor.open()
80-
await self.executor.join(timeout=2)
67+
executor = self._make_executor(target=target)
68+
executor.open()
69+
await executor.join(timeout=2)
8170
self.assertTrue(ran.is_set(), "target never ran")
8271

8372
async def test_target_exception_stops_executor(self):
84-
if _IS_SYNC:
85-
ran = threading.Event()
86-
captured_exc: list = []
87-
orig_excepthook = threading.excepthook
88-
89-
def _capture_excepthook(args):
90-
captured_exc.append(args.exc_value)
91-
92-
threading.excepthook = _capture_excepthook
93-
try:
94-
95-
def target():
96-
ran.set()
97-
raise RuntimeError("error")
98-
99-
self.executor = _make_executor(target=target)
100-
self.executor.open()
101-
self.executor.join(timeout=2)
102-
self.assertTrue(ran.is_set(), "target never ran")
103-
finally:
104-
threading.excepthook = orig_excepthook
105-
self.assertEqual(len(captured_exc), 1)
106-
self.assertIsInstance(captured_exc[0], RuntimeError)
107-
else:
108-
call_count = 0
73+
call_count = 0
10974

110-
async def target():
111-
nonlocal call_count
112-
call_count += 1
113-
raise RuntimeError("error")
75+
async def target():
76+
nonlocal call_count
77+
call_count += 1
78+
raise RuntimeError("error")
11479

115-
self.executor = _make_executor(target=target)
116-
self.executor.open()
117-
await self.executor.join(timeout=2)
118-
self.assertEqual(call_count, 1, "target should stop after exception")
80+
executor = self._make_executor(target=target)
81+
executor.open()
82+
await executor.join(timeout=2)
83+
if executor._task is not None and executor._task.done():
84+
executor._task.exception()
85+
self.assertEqual(call_count, 1, "target should stop after exception")
11986

12087
async def test_skip_sleep_flag_skips_interval(self):
12188
call_times = []
@@ -126,10 +93,10 @@ async def target():
12693
return False
12794
return True
12895

129-
self.executor = _make_executor(interval=30.0, min_interval=0.001, target=target)
130-
self.executor.skip_sleep()
131-
self.executor.open()
132-
await self.executor.join(timeout=3)
96+
executor = self._make_executor(interval=30.0, min_interval=0.001, target=target)
97+
executor.skip_sleep()
98+
executor.open()
99+
await executor.join(timeout=3)
133100
self.assertGreaterEqual(len(call_times), 2)
134101
self.assertLess(call_times[1] - call_times[0], 5.0)
135102

@@ -147,15 +114,15 @@ async def target():
147114
woken.set()
148115
return call_count < 2
149116

150-
self.executor = _make_executor(interval=30.0, min_interval=0.01, target=target)
151-
self.executor.open()
117+
executor = self._make_executor(interval=30.0, min_interval=0.01, target=target)
118+
executor.open()
152119
if _IS_SYNC:
153120
woken.wait(timeout=2)
154121
else:
155122
assert isinstance(woken, asyncio.Event)
156123
await asyncio.wait_for(woken.wait(), timeout=2)
157-
self.executor.wake()
158-
await self.executor.join(timeout=3)
124+
executor.wake()
125+
await executor.join(timeout=3)
159126
self.assertGreaterEqual(call_count, 2)
160127

161128
async def test_open_after_target_returns_false(self):
@@ -166,11 +133,11 @@ async def target():
166133
called += 1
167134
return False
168135

169-
self.executor = _make_executor(target=target)
170-
self.executor.open()
171-
await self.executor.join(timeout=2)
172-
self.executor.open()
173-
await self.executor.join(timeout=2)
136+
executor = self._make_executor(target=target)
137+
executor.open()
138+
await executor.join(timeout=2)
139+
executor.open()
140+
await executor.join(timeout=2)
174141
self.assertGreaterEqual(called, 2)
175142

176143

test/test_periodic_executor.py

Lines changed: 42 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -30,36 +30,27 @@
3030
_IS_SYNC = True
3131

3232

33-
def _make_executor(interval=30.0, min_interval=0.01, target=None, name="test"):
34-
if target is None:
33+
class TestPeriodicExecutor(UnitTest):
34+
def _make_executor(self, interval=30.0, min_interval=0.01, target=None, name="test"):
35+
if target is None:
3536

36-
def target():
37-
return True
38-
39-
return PeriodicExecutor(interval=interval, min_interval=min_interval, target=target, name=name)
40-
41-
42-
class PeriodicExecutorTestBase(UnitTest):
43-
def setUp(self):
44-
self.executor = None
45-
46-
def tearDown(self):
47-
if self.executor is not None:
48-
self.executor.close()
49-
self.executor.join(timeout=2)
37+
def target():
38+
return True
5039

40+
executor = PeriodicExecutor(
41+
interval=interval, min_interval=min_interval, target=target, name=name
42+
)
43+
self.addCleanup(self._close_executor, executor)
44+
return executor
5145

52-
class TestPeriodicExecutor(PeriodicExecutorTestBase):
53-
def test_repr_contains_class_and_name(self):
54-
executor = _make_executor(name="exec")
55-
executor_repr = repr(executor)
56-
self.assertIn("PeriodicExecutor", executor_repr)
57-
self.assertIn("exec", executor_repr)
46+
def _close_executor(self, executor):
47+
executor.close()
48+
executor.join(timeout=2)
5849

5950
def test_join_without_open_is_safe(self):
60-
self.executor = _make_executor()
51+
executor = self._make_executor()
6152
try:
62-
self.executor.join(timeout=0.01)
53+
executor.join(timeout=0.01)
6354
except Exception as e:
6455
self.fail(f"join() raised unexpected Exception {e}")
6556

@@ -73,47 +64,25 @@ def target():
7364
ran.set()
7465
return False
7566

76-
self.executor = _make_executor(target=target)
77-
self.executor.open()
78-
self.executor.join(timeout=2)
67+
executor = self._make_executor(target=target)
68+
executor.open()
69+
executor.join(timeout=2)
7970
self.assertTrue(ran.is_set(), "target never ran")
8071

8172
def test_target_exception_stops_executor(self):
82-
if _IS_SYNC:
83-
ran = threading.Event()
84-
captured_exc: list = []
85-
orig_excepthook = threading.excepthook
86-
87-
def _capture_excepthook(args):
88-
captured_exc.append(args.exc_value)
89-
90-
threading.excepthook = _capture_excepthook
91-
try:
92-
93-
def target():
94-
ran.set()
95-
raise RuntimeError("error")
96-
97-
self.executor = _make_executor(target=target)
98-
self.executor.open()
99-
self.executor.join(timeout=2)
100-
self.assertTrue(ran.is_set(), "target never ran")
101-
finally:
102-
threading.excepthook = orig_excepthook
103-
self.assertEqual(len(captured_exc), 1)
104-
self.assertIsInstance(captured_exc[0], RuntimeError)
105-
else:
106-
call_count = 0
73+
call_count = 0
10774

108-
def target():
109-
nonlocal call_count
110-
call_count += 1
111-
raise RuntimeError("error")
75+
def target():
76+
nonlocal call_count
77+
call_count += 1
78+
raise RuntimeError("error")
11279

113-
self.executor = _make_executor(target=target)
114-
self.executor.open()
115-
self.executor.join(timeout=2)
116-
self.assertEqual(call_count, 1, "target should stop after exception")
80+
executor = self._make_executor(target=target)
81+
executor.open()
82+
executor.join(timeout=2)
83+
if executor._task is not None and executor._task.done():
84+
executor._task.exception()
85+
self.assertEqual(call_count, 1, "target should stop after exception")
11786

11887
def test_skip_sleep_flag_skips_interval(self):
11988
call_times = []
@@ -124,10 +93,10 @@ def target():
12493
return False
12594
return True
12695

127-
self.executor = _make_executor(interval=30.0, min_interval=0.001, target=target)
128-
self.executor.skip_sleep()
129-
self.executor.open()
130-
self.executor.join(timeout=3)
96+
executor = self._make_executor(interval=30.0, min_interval=0.001, target=target)
97+
executor.skip_sleep()
98+
executor.open()
99+
executor.join(timeout=3)
131100
self.assertGreaterEqual(len(call_times), 2)
132101
self.assertLess(call_times[1] - call_times[0], 5.0)
133102

@@ -145,15 +114,15 @@ def target():
145114
woken.set()
146115
return call_count < 2
147116

148-
self.executor = _make_executor(interval=30.0, min_interval=0.01, target=target)
149-
self.executor.open()
117+
executor = self._make_executor(interval=30.0, min_interval=0.01, target=target)
118+
executor.open()
150119
if _IS_SYNC:
151120
woken.wait(timeout=2)
152121
else:
153122
assert isinstance(woken, asyncio.Event)
154123
asyncio.wait_for(woken.wait(), timeout=2)
155-
self.executor.wake()
156-
self.executor.join(timeout=3)
124+
executor.wake()
125+
executor.join(timeout=3)
157126
self.assertGreaterEqual(call_count, 2)
158127

159128
def test_open_after_target_returns_false(self):
@@ -164,11 +133,11 @@ def target():
164133
called += 1
165134
return False
166135

167-
self.executor = _make_executor(target=target)
168-
self.executor.open()
169-
self.executor.join(timeout=2)
170-
self.executor.open()
171-
self.executor.join(timeout=2)
136+
executor = self._make_executor(target=target)
137+
executor.open()
138+
executor.join(timeout=2)
139+
executor.open()
140+
executor.join(timeout=2)
172141
self.assertGreaterEqual(called, 2)
173142

174143

0 commit comments

Comments
 (0)