🐞描述错误
当aiohttp中的某些对象泄漏时,它们将调用asyncio异常处理程序,这在某些环境中(例如,在使用aiorun时,使用stop_on_unhandled_errors=True
)意味着整个程序都已停止。
例如,这是ClientResponse
的相关代码:
💡重现
泄漏执行此操作的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
我不同意。
未封闭的资源是一个足够严重的编程错误,可以通过编写正确的代码轻松修复该错误。
例如,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停止循环的原因。