在Python中的异步IO任务上拦截对_wait _;的调用

在Python中的异步IO任务上拦截对_wait _;的调用,python,python-asyncio,Python,Python Asyncio,我想做的是在以下情况下得到通知: 创建了一个异步IO任务 任何人都在等待异步IO任务 第一个很简单——使用任务工厂。 对于第二种方法,我可以想到两种实现方法: 1) 用一个行为类似于未来的新Python对象包装任务(足以愚弄其他代码),将任何必要的方法转发给包装的任务 2) 猴子补丁等待在原始任务中通知我,然后将其转发给原始的uu等待 我试着先选择选项(2),因为它看起来更容易。然而,我不能让它工作。试图修改wait(直接或通过setattr)似乎没有任何效果 问题: 有什么办法去上班吗

我想做的是在以下情况下得到通知:

  • 创建了一个异步IO任务
  • 任何人都在等待异步IO任务
第一个很简单——使用任务工厂。 对于第二种方法,我可以想到两种实现方法:

1) 用一个行为类似于未来的新Python对象包装任务(足以愚弄其他代码),将任何必要的方法转发给包装的任务

2) 猴子补丁等待在原始任务中通知我,然后将其转发给原始的uu等待

我试着先选择选项(2),因为它看起来更容易。然而,我不能让它工作。试图修改wait(直接或通过setattr)似乎没有任何效果

问题:

  • 有什么办法去上班吗
  • (1)方法有多安全
  • 是否有一种“干净”的方式来包装我可以使用的未来任务
这里有一些代码显示了我尝试做的事情

import asyncio

def patch_task_factory(loop):
    prior = loop.get_task_factory()
    def factory(loop, coro):
        if prior is None:
            task = asyncio.Task(coro, loop=loop)
        else:
            task = prior(loop, coro)

        # here we know a task has been created
        # but to also get notified when someone waits for it, we need to patch __await__
        orig_await = task.__await__

        def intercepted_await(self):
            print("someone is awaiting this task")
            # should work because we're just returning the iterator from the real await
            return orig_await(self)
        task.__await__ = intercepted_await.__get__(task, asyncio.Task)

        return task
    loop.set_task_factory(factory)


async def test1():
    print("test1!")
    return 1

async def test2():
    print("test2!")
    return await asyncio.create_task(test1())


def main():
    loop = asyncio.get_event_loop()
    patch_task_factory(loop)
    res = asyncio.run(test2())
    print(res)


if __name__ == "__main__":
    # execute only if run as a script
    main()

问题在于
任务
是一个内置/扩展方法,从
\u asyncio
导入(注意下划线)。您会注意到,例如,
setattr(task.\uuuu class.\uuuu.\uuuu getattribute.\uuuuuuu…)
抱怨您无法修改内置类的方法包装器

在这种情况下,您可以考虑代理对象。注意覆盖

\uuuu wait\uuuu
的“诡计”:

proxy.py
直接取自
http://code.activestate.com/recipes/496741-object-proxying/

$ cat task_factory.py

为什么您需要此类非标准通知?这里的“x”是什么?计算异步任务中发生的某些事件的“之前发生”关系。计算中涉及的图形的构建部分依赖于这两个事件,尽管还有其他事件。显然,对于一般情况,不可能有一个完美的解决方案,但我仍然需要在“尽最大努力”的基础上这样做。顺便说一句,我今天已经取得了很多进展。设法采用“用未来包装任务”的方法。不过,监视wait本身还不够好,因为它不包括as_completed或gather的使用。
#!/usr/bin/env python3

import asyncio
from typing import Any, Generator
from proxy import Proxy


class ProxyWithAwait(Proxy):
    def __await__(self) -> Generator[Any, None, Any]:
        _obj = object.__getattribute__(self, "_obj")
        aw = _obj.__await__()
        print("*** INTERCEPTING AWAIT: BEFORE")
        yield aw.send(None)
        print("*** INTERCEPTING AWAIT: AFTER")
        aw.close()


async def main() -> None:

    loop = asyncio.get_event_loop()
    old_task_factory = loop.get_task_factory()

    def my_task_factory(
        loop: asyncio.AbstractEventLoop,
        coro: Generator[Any, None, Any],
    ) -> asyncio.Future:
        if old_task_factory is None:
            t = asyncio.tasks.Task(coro, loop=loop)
        else:
            t = old_task_factory(loop, coro)

        return ProxyWithAwait(t)

    loop.set_task_factory(my_task_factory)

    async def my_task_func() -> None:
        print("my_task_func(): start")
        await asyncio.sleep(1.5)
        print("my_task_func(): end")

    t = loop.create_task(my_task_func())
    print("main: before await t")
    await t
    print("main: after await t")

    loop.set_task_factory(old_task_factory)


if __name__ == "__main__":
    asyncio.run(main())