summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbd <bdunahu@operationnull.com>2025-07-21 13:25:56 -0600
committerbd <bdunahu@operationnull.com>2025-07-21 13:25:56 -0600
commit487c77a96504c0178581a7a4bb7e9cd66783548f (patch)
tree67bb104ce5fe7238546001a47a85b8aaafc042a1
parent83e88319b72df01fa70e6bf3a020ab745cdef661 (diff)
Update comments reflecting on asyncio.gather and task groups
-rwxr-xr-xaergia/aergia.py3
-rw-r--r--t/test_functionality.py79
2 files changed, 59 insertions, 23 deletions
diff --git a/aergia/aergia.py b/aergia/aergia.py
index 83cf0fa..b10c82b 100755
--- a/aergia/aergia.py
+++ b/aergia/aergia.py
@@ -251,7 +251,6 @@ class Aergia(object):
ret = None
while curr:
frame = getattr(curr, 'cr_frame', None)
- # print(frame)
if frame and Aergia.should_trace(frame.f_code.co_filename):
ret = frame
curr = getattr(curr, 'cr_await', None)
@@ -260,7 +259,7 @@ class Aergia(object):
@staticmethod
def should_trace(filename):
'''Returns FALSE if filename is uninteresting to the user.
- Don't depend on this.'''
+ Don't depend on this. It's good enough for testing.'''
# FIXME Assume GuixSD. Makes filtering easy
if '/gnu/store' in filename:
return False
diff --git a/t/test_functionality.py b/t/test_functionality.py
index 63863cf..75f0137 100644
--- a/t/test_functionality.py
+++ b/t/test_functionality.py
@@ -3,6 +3,10 @@ import asyncio
import threading
+class TerminateTaskGroup(Exception):
+ '''Exception raised to terminate a task group.'''
+
+
class BasicUsage(utils.AergiaUnitTestCase):
def test_asyncless(self):
@@ -55,7 +59,10 @@ class BasicUsage(utils.AergiaUnitTestCase):
self.assertFuncContains('b', [self.expected_samples(delay * 3)],
samples)
- # TODO samples from gather all execution time, should we trace this?
+ # the gather function is technically waiting for all tasks to finish.
+ # This might be seen as unintuitive though I don't want to bias the
+ # results by adding logic to add artificial consistency.
+ # profiling does not mean obscuring implementation details.
self.assertFuncContains('a', [self.expected_samples(delay)], samples)
def test_subthread_task(self):
@@ -75,7 +82,7 @@ class BasicUsage(utils.AergiaUnitTestCase):
self.assertFuncContains('c', [], samples)
self.assertFuncContains('b', [self.expected_samples(delay * 3)],
samples)
- # TODO samples from gather all execution time, should we trace this?
+ # see comment on `test_simultaneous_tasks'.
self.assertFuncContains('a', [self.expected_samples(delay)], samples)
def test_eager_task(self):
@@ -92,6 +99,31 @@ class BasicUsage(utils.AergiaUnitTestCase):
samples = self.Aergia.get_samples()
self.assertFuncContains('a', [self.expected_samples(delay)], samples)
+ def test_async_eager_scheduled_woven(self):
+ d1 = 0.2
+ d2 = 0.3
+ d3 = 0.2
+ d4 = 0.1
+
+ async def a():
+ await asyncio.sleep(d1)
+ proc = await asyncio.create_subprocess_shell(f'sleep {d2}')
+ await proc.communicate()
+ await asyncio.sleep(d3)
+ proc = await asyncio.create_subprocess_shell(f'sleep {d4}')
+ await proc.communicate()
+
+ self.Aergia.start()
+ asyncio.run(a())
+ self.Aergia.stop()
+
+ samples = self.Aergia.get_samples()
+ self.assertFuncContains('a', [self.expected_samples(d1),
+ self.expected_samples(d2),
+ self.expected_samples(d3),
+ self.expected_samples(d4),
+ ], samples)
+
def test_async_generator(self):
delay = 0.2
num_times = 10
@@ -111,8 +143,8 @@ class BasicUsage(utils.AergiaUnitTestCase):
self.Aergia.stop()
samples = self.Aergia.get_samples()
- # TODO can we make these results more intuitive?
- # async generators also do not work at all with yappi.
+ # TODO I do not think async generators report correctly.
+ # also does not work at all with yappi.
# doing so would be unique to this profiler.
# self.assertFuncContains('b',
@@ -142,8 +174,8 @@ class BasicUsage(utils.AergiaUnitTestCase):
self.Aergia.stop()
samples = self.Aergia.get_samples()
- # TODO can we make these results more intuitive?
- # async generators also do not work at all with yappi.
+ # TODO I do not think async generators report correctly.
+ # also does not work at all with yappi.
# doing so would be unique to this profiler.
self.assertFuncContains('b', [], samples)
@@ -192,28 +224,32 @@ class BasicUsage(utils.AergiaUnitTestCase):
self.assertFuncContains('d', [self.expected_samples(d1)], samples)
self.assertFuncContains('c', [self.expected_samples(d2)], samples)
self.assertFuncContains('b', [self.expected_samples(d3)], samples)
- # TODO where does this come from?
+ # the task group is technically waiting for all tasks to finish.
+ # same as task.gather
+ # This might be seen as unintuitive, (especially considering how
+ # the next test works), though I don't want to bias the results
+ # by adding logic to add artificial consistency.
+ # Both are reporting correctly.
self.assertFuncContains('a', [self.expected_samples(d2)], samples)
def test_task_groups_cancel(self):
d1 = 0.1
- d2 = 0.2
- d3 = 0.3
- async def d(): await asyncio.sleep(d1)
+ d2 = 0.3
+ d3 = 0.2
+ async def d(): raise TerminateTaskGroup
- async def c():
- await asyncio.sleep(d2)
- 1 / 0 # crash
+ async def c(): await asyncio.sleep(d1)
- async def b(): await asyncio.sleep(d3)
+ async def b(): await asyncio.sleep(d2)
async def a():
try:
async with asyncio.TaskGroup() as tg:
- tg.create_task(d())
tg.create_task(c())
tg.create_task(b())
- except:
+ await asyncio.sleep(d3)
+ tg.create_task(d())
+ except* TerminateTaskGroup:
pass
self.Aergia.start()
@@ -222,8 +258,9 @@ class BasicUsage(utils.AergiaUnitTestCase):
samples = self.Aergia.get_samples()
- self.assertFuncContains('d', [self.expected_samples(d1)], samples)
- self.assertFuncContains('c', [self.expected_samples(d2)], samples)
- self.assertFuncContains('b', [self.expected_samples(d2)], samples)
- # TODO where does this come from?
- self.assertFuncContains('a', [self.expected_samples(d2)], samples)
+ self.assertFuncContains('d', [], samples)
+ self.assertFuncContains('c', [self.expected_samples(d1)], samples)
+ self.assertFuncContains('b', [self.expected_samples(d3)], samples)
+ # this time is attached to the sleep call itself. Aergia's print
+ # function would confirm this!
+ self.assertFuncContains('a', [self.expected_samples(d3)], samples)