<p>aiohttp不应在异常情况下调用异常处理程序</p>

创建于 2020-11-10  ·  4评论  ·  资料来源: aio-libs/aiohttp

🐞描述错误
当aiohttp中的某些对象泄漏时,它们将调用asyncio异常处理程序,这在某些环境中(例如,在使用aiorun时,使用stop_on_unhandled_errors=True )意味着整个程序都已停止。

例如,这是ClientResponse的相关代码:

https://github.com/aio-libs/aiohttp/blob/a8d9ec3f1667463e80545b1cacc7833d1ff305e9/aiohttp/client_reqrep.py#L748 -L751

💡重现
泄漏执行此操作的aiohttp对象。

💡预期行为
泄漏的对象不应引起异常,原因与open()对文件而不是.close()对文件不会引发异常的原因相同。

所有执行此操作的对象似乎已经发出了有关资源泄漏的警告-我也看不到它们调用异常处理程序的原因。

📋您的Python版本

$ python --version
Python 3.8.6

📋您的aiohttp / yarl / multidict发行版

$ python -m pip show aiohttp
Name: aiohttp
Version: 3.6.2

控制台
$ python -m pip显示multidict
名称:multidict
版本:4.7.6

```console
$ python -m pip show yarl
Name: yarl
Version: 1.5.1

📋其他背景
cjrh / aiorun#56

bug wontfix

所有4条评论

我不同意。
未封闭的资源是一个足够严重的编程错误,可以通过编写正确的代码轻松修复该错误。
例如,asyncio本身使用call_exception_handler()来报告有关未等待的异步调用。
如果您不想编写学究的代码,可以使用stop_on_unhandled_errors=False

那么,为什么不提出一个真正的例外呢?

我猜是因为在__del__引发异常将被忽略。 但这是应该的,不是吗? 调试“某些东西分配了一个对象并没有正确关闭它”是非常困难的,尤其是当该错误不在您自己的代码中,而是在某些库中时; source_traceback上下文键是未记录的,依靠asyncio的默认异常处理程序的未记录的行为来调试内存泄漏是不友好的。

我也看不到asyncio在哪里使用call_exception_handler()来警告未等待的协程。 当未等待的协程被垃圾回收时,它会调用_PyErr_WarnUnawaitedCoroutine

    /* If `gen` is a coroutine, and if it was never awaited on,
       issue a RuntimeWarning. */
    if (gen->gi_code != NULL &&
        ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
        gen->gi_frame->f_lasti == -1)
    {
        _PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
    }

调用warnings._warn_unawaited_coroutine()

void
_PyErr_WarnUnawaitedCoroutine(PyObject *coro)
{
    /* First, we attempt to funnel the warning through
       warnings._warn_unawaited_coroutine.

它发出一个RuntimeWarning。 我看不到call_exception_handler()在那里使用的任何位置–实际上,我看不到stdlib中没有使用call_exception_handler()的上下文,而在上下文中不包含exception

再说一次:如果忘记关闭文件不会引发异常,为什么还要忘记清理随机的aiohttp对象呢?

没错,不建议使用__del__方法引发异常。
这就是为什么我们需要另一个信令解决方案。
Asyncio确实__del__调用call_exception_handler() ,例如参见Task.__del__https :

没错, source_traceback没有记录在https://docs.python.org/3/library/asyncio-eventloop.html?highlight=call_exception_handler#asyncio.loop.call_exception_handler
您介意在https://github.com/python/cpython/上创建拉取请求以修复文档吗? 我很乐意查看/合并。

再说一次:如果忘记关闭文件不会引发异常,为什么还要忘记清理随机的aiohttp对象呢?

aiohttp也不会从__del__引发异常,这在Python中是不可能的,因为对象终结器的调用是不确定的。

未关闭的文件引发ResourceWarning 。 默认情况下会忽略此警告: https: //docs.python.org/3/library/warnings.html#default -warning-filter
这就是为什么如果许多开发人员未明确启用它们,则这种警告对许多开发人员不可见。 之所以这样做,部分原因是出于历史原因:很长时间以来,这种警告就不存在了,许多图书馆不调用file.close()因为它们的作者并不了解需要进行优雅的资源清理等。 。

幸运的是, file.close()可以从被称为file.__del__安全的,这是正常通话。
但是在异步世界中, close()经常是不同的:它必须是一个异步函数,不能从__del__调用它。
例如, transport.close()无需等待protocol.connection_lost()导致即使是普通插座警告; 对于SSL,情况甚至更糟。
aiohttp与同步/异步终结器有些混乱,但是我正在研究它。 aiohttp 4.0应该被大量清理。

这就是清理非常重要的原因,应该发出警告,并使用call_exception_handler()来使不良用法更加明显。

Asyncio _does_从__del__调用call_exception_handler() Task.__del__ ,例如,请参阅

公平地说,我错过了那个电话。

您介意在python / cpython上创建拉取请求以修复文档吗?

老实说,我不确定如何最好地记录下来。 我创建了bpo-42347,因为我注意到文档,文档字符串和实现之间存在一些其他差异。

这就是清理非常重要的原因,应该发出警告,并使用call_exception_handler()使错误用法更加明显。

我猜想,当您收到错误消息时,这简直令人沮丧,因为您正在使用的库无法正确地对其自身进行清理。 我没有意识到应该停止应用程序如此重要,而且我最初甚至没有意识到泄漏是aiorun停止循环的原因。

此页面是否有帮助?
0 / 5 - 0 等级