![](/img/trans.png)
[英]Python3: Reportlab Image - ResourceWarning: unclosed file <_io.BufferedReader name=…>
[英]ResourceWarning: unclosed file <_io.BufferedReader name=4>
考虑以下程序:
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()
运行它会产生以下 output:
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()
object 关联的流。 For your example, that's the Popen.stdout
stream, created because you instructed the Popen Popen()
object to create a pipe for the child process standard output. 最简单的方法是使用 Popen Popen()
object 作为上下文管理器:
with subprocess.Popen(['cat'], stdout=subprocess.PIPE, stdin=f) as p:
yield p.stdout.readline()
p.kill()
在关闭句柄后,我放弃了p.wait()
调用,因为 Popen Popen.__exit__()
为您处理了这个问题。
如果您想进一步找出导致资源警告的确切原因,那么我们可以从警告告诉我们的内容开始,并通过设置PYTHONTRACEMALLOC
环境变量来启用tracemalloc
模块:
$ 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 行指向 subprocess.py 中的subprocess.py
行:
if c2pread != -1:
self.stdout = io.open(c2pread, 'rb', bufsize)
c2pread
is the file handle for one half of a os.pipe()
pipe object created to handle communication from child process to Python parent process (created by the Popen._get_handles()
utility method , because you set stdout=PIPE
). io.open()
与内置的open()
function 完全相同。 所以这是创建BufferedIOReader
实例的地方,它充当 pipe 的包装器,以接收子进程生成的 output。
您还可以显式关闭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
、 stdout
或stderr
管道,它都会继续正常工作。
请注意,大多数subprocess
代码示例不这样做,因为它们倾向于使用Popen.communicate()
,它会为您关闭文件句柄。
运行单元测试时,我还收到了一些ResourceWarning: unclosed file <_io.FileIO name=7 mode='wb' closefd=True>
消息。 我不能使用Popen
object 作为上下文管理器,所以我在我的类__del__
方法中这样做了:
import subprocess
class MyClass:
def __init__(self):
self.child= subprocess.Popen(['dir'],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
def __del__(self):
self.child.terminate()
self.child.communicate()
您只需将my_fn
更改为使用with
,就不需要kill
和wait
。 Python 会为你关闭它:
def my_fn(f):
with subprocess.Popen(['cat'], stdout=subprocess.PIPE, stdin=f) as p:
yield p.stdout.readline()
它以如下方式解决了这个问题:
def my_test():
with tempfile.TemporaryFile() as f:
x = my_fn(f)
l = list()
l.append(x)
# print(l)
或者如下方式:
def my_test():
with tempfile.TemporaryFile() as f:
l = list([my_fn(f)])
以下对这个问题有帮助:
list Found at: builtins
list() -> new empty list
list(iterable) -> new list initialized from iterable's
items
https://docs.python.org/3/library/stdtypes.html#list提到了以下内容:
class list([iterable])
Lists may be constructed in several ways:
* Using a pair of square brackets to denote the empty list: []
* Using square brackets, separating items with commas: [a], [a, b, c]
* Using a list comprehension: [x for x in iterable]
* Using the type constructor: list() or list(iterable)
The constructor builds a list whose items are the same and in the same order as
iterable’s items. iterable may be either a sequence, a container that supports
iteration, or an iterator object. If iterable is already a list, a copy is made and
returned, similar to iterable[:]. For example, list('abc') returns ['a', 'b', 'c']
and list( (1, 2, 3) ) returns [1, 2, 3]. If no argument is given, the constructor
creates a new empty list, [].
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.