`shutil.rmtree`对`tempfile.TemporaryDirectory()`不起作用

时间:2018-06-01 19:06:49

标签: python python-3.x python-unittest temporary-files shutil

考虑这个测试

import shutil, tempfile
from os import path
import unittest

from pathlib import Path

class TestExample(unittest.TestCase):
    def setUp(self):
        # Create a temporary directory
        self.test_dir = tempfile.TemporaryDirectory()
        self.test_dir2 = tempfile.mkdtemp()

    def tearDown(self):
        # Remove the directory after the  test
        shutil.rmtree(self.test_dir2) 
        shutil.rmtree(self.test_dir.name) #throws error

    def test_something(self):
        self.assertTrue(Path(self.test_dir.name).is_dir())
        self.assertTrue(Path(self.test_dir2).is_dir())

if __name__ == '__main__':
    unittest.main()

但是在tearDown中会出现错误

FileNotFoundError: [Errno 2] No such file or directory: '/tmp/tmpxz7ts7a7'

指的是self.test_dir.name

根据the source code for tempfile,两个元素都是相同的。

    def __init__(self, suffix=None, prefix=None, dir=None):
        self.name = mkdtemp(suffix, prefix, dir)
        self._finalizer = _weakref.finalize(
            self, self._cleanup, self.name,
            warn_message="Implicitly cleaning up {!r}".format(self))

我没有在上下文中使用它,所以{I} __exit__()不应该被调用。

发生了什么事?

2 个答案:

答案 0 :(得分:5)

不要使用shutil清除这些内容。 tempfile.TemporaryDirectory类提供了cleanup()方法,如果您想要选择加入明确的清理,只需调用它。

你的代码崩溃的原因是TemporaryDirectory类被设计为一旦超出范围(ref count为零)就自行清理。但是,由于您已经手动从文件系统中删除了目录,因此当实例随后尝试删除自身时,拆除将失败。 "没有这样的文件或目录"错误来自TemporaryDirectory自己的拆除,而不是来自shutil.rmtree行!

答案 1 :(得分:5)

与上下文无关:

import tempfile,os

t = tempfile.TemporaryDirectory()
s = t.name
print(os.path.isdir(s))
# os.rmdir(s) called here triggers error on the next line
t = None
print(os.path.isdir(s))

打印

True
False

因此,只要t的引用设置为None,就会对对象进行垃圾回收并删除目录,如documentation所示:

  

完成上下文或销毁临时目录对象后,新创建的临时目录及其所有内容将从文件系统中删除。

取消注释下面的代码段中的os.rmdir(s)会在对象完成时抛出异常:

Exception ignored in: <finalize object at 0x20b20f0; dead>
Traceback (most recent call last):
  File "L:\Python34\lib\weakref.py", line 519, in __call__
    return info.func(*info.args, **(info.kwargs or {}))
  File "L:\Python34\lib\tempfile.py", line 698, in _cleanup
    _shutil.rmtree(name)
  File "L:\Python34\lib\shutil.py", line 482, in rmtree
    return _rmtree_unsafe(path, onerror)
  File "L:\Python34\lib\shutil.py", line 364, in _rmtree_unsafe
    onerror(os.listdir, path, sys.exc_info())
  File "L:\Python34\lib\shutil.py", line 362, in _rmtree_unsafe
    names = os.listdir(path)

所以你的调用可能会成功,但是你在对象的最终确定中获得了异常(之后)

调用cleanup()对象方法而不是rmtree解决了这个问题,因为对象内部状态已更新为而不是以在最终确定时尝试删除目录(如果您要求我,对象应该测试目录是否存在,然后再尝试清理它,但即使这样也不总是有效,因为它不是原子操作)

所以替换

shutil.rmtree(self.test_dir.name)

通过

self.test_dir.cleanup()

或者什么都不做,让对象在删除时清理目录。