Skip to content

Commit

Permalink
3.12 prep
Browse files Browse the repository at this point in the history
Summary:
Make tests work on 3.12 and decouple later.TestCase from the internals of asyncio.all_tasks()

cleaned up test cases so we didn't inadvertently del purposefully "leaked" tasks

Reviewed By: aleivag

Differential Revision: D51734106

fbshipit-source-id: 6cb73449e4732c9a899f4687162459618742991c
  • Loading branch information
fried authored and facebook-github-bot committed Dec 1, 2023
1 parent 9b8dfb5 commit cf7b2bb
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 29 deletions.
33 changes: 13 additions & 20 deletions later/tests/unittest/test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,48 +15,43 @@

import asyncio
import unittest
from typing import Any, Optional
from typing import Any, List

from later.unittest import ignoreAsyncioErrors, ignoreTaskLeaks, TestCase


saved_task: Optional[asyncio.Task[Any]] = None
saved_done_task: Optional[asyncio.Task[Any]] = None
# This is a place to purposefully produce leaked tasks
saved_tasks: List[asyncio.Task[Any]] = []


class TestTestCase(TestCase):
@unittest.expectedFailure
async def test_unmanaged_task_created_in_testmethod(self) -> None:
global saved_task
saved_task = asyncio.get_running_loop().create_task(asyncio.sleep(10))
saved_tasks.append(asyncio.get_running_loop().create_task(asyncio.sleep(10)))

async def test_managed_task_done(self) -> None:
global saved_done_task
saved_done_task = asyncio.get_running_loop().create_task(
asyncio.sleep(0.5, "test")
)
await saved_done_task
task = asyncio.get_running_loop().create_task(asyncio.sleep(0.5, "test"))
saved_tasks.append(task)
await task

async def test_managed_task_done_none(self) -> None:
global saved_done_task
async def test_unmanaged_task_done_none(self) -> None:
"""None value Done tasks are ignored"""

async def coro(e: asyncio.Event) -> None:
e.set()

event = asyncio.Event()
saved_done_task = asyncio.get_running_loop().create_task(coro(event))
saved_tasks.append(asyncio.get_running_loop().create_task(coro(event)))
await event.wait()

@unittest.expectedFailure
async def test_unmanaged_task_done_value(self) -> None:
global saved_done_task

async def coro(e: asyncio.Event) -> bool:
e.set()
return False

event = asyncio.Event()
saved_done_task = asyncio.get_running_loop().create_task(coro(event))
saved_tasks.append(asyncio.get_running_loop().create_task(coro(event)))
await event.wait()

@unittest.expectedFailure
Expand Down Expand Up @@ -106,8 +101,7 @@ async def test_ignore_task_leaks(self) -> None:
async def coro():
raise RuntimeError

# pyre-fixme[16]: `TestTestCase` has no attribute `_task`.
self._task = asyncio.get_running_loop().create_task(coro())
saved_tasks.append(asyncio.get_running_loop().create_task(coro()))

async def test_forgotten_tasks_done_no_value(self) -> None:
asyncio.get_running_loop().create_task(asyncio.sleep(0))
Expand All @@ -129,8 +123,7 @@ async def test_ignore_task_leaks_on_case_class(self) -> None:
async def coro():
raise RuntimeError

# pyre-fixme[16]: `IgnoreTaskLeaksTestCase` has no attribute `_task`.
self._task = asyncio.get_running_loop().create_task(coro())
saved_tasks.append(asyncio.get_running_loop().create_task(coro()))


if __name__ == "__main__":
Expand Down
16 changes: 7 additions & 9 deletions later/unittest/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ def __repr__(self) -> str:
return f"<{self.__class__.__name__} {' '.join(repr_info)}>"

def _mark_managed(self):
if not self._managed:
self._managed = True
_unmanaged_tasks.discard(self)
self._managed = True

def __await__(self):
self._mark_managed()
Expand Down Expand Up @@ -136,19 +134,18 @@ def all_tasks(loop):

# This is copied from the guts of asyncio.all_tasks
# Looping the WeakSet since it is possible it fails during iteration
_all_tasks = asyncio.tasks._all_tasks
_get_loop = asyncio.futures._get_loop
i = 0
while True:
try:
tasks = list(_all_tasks) + list(_unmanaged_tasks)
tasks = list(_unmanaged_tasks)
except RuntimeError: # pragma: nocover
i += 1
if i >= 1000:
raise
else:
break
return {t for t in tasks if _get_loop(t) is loop}
else:
break
return {t for t in tasks if _get_loop(t) is loop}


def ignoreAsyncioErrors(test_item: _F) -> _F:
Expand Down Expand Up @@ -206,4 +203,5 @@ def _callTestMethod(self, testMethod) -> None:
if left_over_tasks and not ignore_tasks:
self.assertEqual(set(), left_over_tasks, "left over un-awaited tasks!")
if error.called and not ignore_error:
self.fail(f"asyncio logger.error() was called!\n{error.call_args_list}")
errors = "\n\n".join(c[0][0] for c in error.call_args_list)
self.fail(f"asyncio logger.error() was called!\n{errors}")

0 comments on commit cf7b2bb

Please sign in to comment.