当我在Python3上使用Pillow(版本3.3.0,通过pip安装)将图像数据加载到numpy数组时,我的单元测试会报告ResourceWarning
。例如,当我运行以下脚本时出现警告:
#! /usr/bin/env python3
import unittest
import numpy as np
import PIL.Image
def load_image():
with PIL.Image.open('test.tif') as im:
return np.array(im)
class TestData(unittest.TestCase):
def test_PIL(self):
im = load_image()
print(im.shape)
unittest.main()
输出
./err.py:14: ResourceWarning: unclosed file <_io.BufferedReader name='test.tif'>
im = load_image()
(420, 580)
.
----------------------------------------------------------------------
Ran 1 test in 0.012s
OK
(如果我没有将图像包裹在一个numpy数组中,警告就会消失。)
此资源警告是否表示我的代码泄漏(例如,除了使用with
语句之外,我是否需要以某种方式“关闭”图像文件)?或者,如果警告是虚假的,我该如何禁用它?
答案 0 :(得分:5)
TL; DR ,我认为这是一个错误。这应该按预期关闭文件句柄:
def load_image():
with open('test.tif', 'rb') as im_handle:
im = PIL.Image.open(im_handle)
return np.array(im)
好的,让我们检查一下实际发生了什么:
为此我们添加一个记录器:
#! /usr/bin/env python3
import logging
logging.getLogger().setLevel(logging.DEBUG)
logging.debug('hi from the logger!')
import unittest
import numpy as np
import PIL.Image
def load_image():
with PIL.Image.open('test.tif') as im:
return np.array(im)
class TestData(unittest.TestCase):
def test_PIL(self):
im = load_image()
print(im.shape)
unittest.main()
这就是我们得到的:
DEBUG:root:hi from the logger!
/Users/ch/miniconda/envs/sci34/lib/python3.4/site-packages/PIL/Image.py:678: ResourceWarning: unclosed file <_io.BufferedReader name='test.tif'>
self.load()
DEBUG:PIL.Image:Error closing: 'NoneType' object has no attribute 'close'
(225, 300, 3)
.
----------------------------------------------------------------------
Ran 1 test in 0.022s
OK
DEBUG:PIL.Image:Error closing: 'NoneType' object has no attribute 'close'
在Image
类的close方法中引发:
def close(self):
"""
Closes the file pointer, if possible.
This operation will destroy the image core and release its memory.
The image data will be unusable afterward.
This function is only required to close images that have not
had their file read and closed by the
:py:meth:`~PIL.Image.Image.load` method.
"""
try:
self.fp.close()
except Exception as msg:
logger.debug("Error closing: %s", msg)
# Instead of simply setting to None, we're setting up a
# deferred error that will better explain that the core image
# object is gone.
self.im = deferred_error(ValueError("Operation on closed image"))
用文字说:
在with
块的末尾,调用close
方法。它尝试关闭存储在self.fp
中的打开文件句柄。但在您的情况下,self.fp
不是文件句柄,因此无法关闭。结局无声地失败。
因此,当您离开with
块时,文件句柄未关闭且鼻子的错误消息是合法的。