python - 资源警告 : unclosed file <_io. BufferedReader 名称=4>

标签 python

考虑以下程序:

import tempfile
import unittest
import subprocess

def my_fn(f):
    p = subprocess.Popen(['cat'], stdout=subprocess.PIPE, stdin=f)
    yield p.stdout.readline()
    p.kill()
    p.wait()

def my_test():
    with tempfile.TemporaryFile() as f:
        l = list(my_fn(f))

class BuggyTestCase(unittest.TestCase):
    def test_my_fn(self):
        my_test()

my_test()
unittest.main()

运行它会产生以下输出:

a.py:13: ResourceWarning: unclosed file <_io.BufferedReader name=4>
  l = list(my_fn(f))
ResourceWarning: Enable tracemalloc to get the object allocation traceback
.
----------------------------------------------------------------------
Ran 1 test in 0.005s

OK

警告的实际原因是什么以及如何解决?请注意,如果我注释掉 unittest.main() 问题就会消失,这意味着它特定于 subprocess+unittest+tempfile。

最佳答案

您应该关闭与您打开的 Popen() 对象关联的流。对于您的示例,这是 Popen.stdout 流,因为您指示 Popen() 对象为子进程标准输出创建管道而创建。最简单的方法是使用 Popen() 对象作为上下文管理器:

with subprocess.Popen(['cat'], stdout=subprocess.PIPE, stdin=f) as p:
    yield p.stdout.readline()
    p.kill()

我放弃了 p.wait() 调用,因为 Popen.__exit__() 在关闭句柄后会为您处理此问题。

如果您想进一步弄清楚导致资源警告的确切原因,那么我们可以从警告告诉我们的事情开始,enable the tracemalloc module通过设置PYTHONTRACEMALLOC environment variable :

$ PYTHONTRACEMALLOC=1 python a.py
a.py:13: ResourceWarning: unclosed file <_io.BufferedReader name=4>
  l = list(my_fn(f))
Object allocated at (most recent call last):
  File "/.../lib/python3.8/subprocess.py", lineno 844
    self.stdout = io.open(c2pread, 'rb', bufsize)
.
----------------------------------------------------------------------
Ran 1 test in 0.019s

OK

因此警告是由 subprocess 模块打开的文件引发的。我在这里使用 Python 3.8.0,因此跟踪中的第 844 行指向 these lines in subprocess.py :

if c2pread != -1:
    self.stdout = io.open(c2pread, 'rb', bufsize)

c2preados.pipe() pipe object 的一半的文件句柄创建用于处理从子进程到 Python 父进程的通信(由 Popen._get_handles() utility method 创建,因为您设置了 stdout=PIPE)。 io.open() 与内置的 open() function 完全相同。 。因此,这就是创建 BufferedIOReader 实例的地方,充当管道的包装器来接收子进程产生的输出。

您还可以显式关闭p.stdout:

p = subprocess.Popen(['cat'], stdout=subprocess.PIPE, stdin=f)
yield p.stdout.readline()
p.stdout.close()
p.kill()
p.wait()

或使用p.stdout作为上下文管理器:

p = subprocess.Popen(['cat'], stdout=subprocess.PIPE, stdin=f)
with p.stdout:
    yield p.stdout.readline()
p.kill()
p.wait()

但是始终使用subprocess.Popen()作为上下文管理器会更容易,因为无论有多少stdin,它都会继续正常工作stdoutstderr 您创建的管道。

请注意,大多数 subprocess 代码示例不会执行此操作,因为它们倾向于使用 Popen.communicate() ,它会为您关闭文件句柄。

关于python - 资源警告 : unclosed file <_io. BufferedReader 名称=4>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58649679/

相关文章:

python - Numpy 数组串联

python - 什么是 Python 中的兄弟类?

python - (python 3) 从 .h264 自动转换为 .mp4

python - 退出 PyQT 应用程序时出现段错误

python - Selenium Chrome 每隔一段时间就挂一次

python - OpenCV + Numpy 脚本

python - 我如何通过电子邮件将来自 Flask 的错误日志发送给自己?

python - Python 函数中赋值之前引用的变量

python - .write 在 Python 中不起作用

python - 用户在自定义模块中编辑 many2one 字段时出现 AccessError