`shutil.rmtree` 不适用于 `tempfile.TemporaryDirectory()`

`shutil.rmtree` does not work on `tempfile.TemporaryDirectory()`

考虑这个测试

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))

而且我没有在上下文中使用它,因此据我所知不应调用 __exit__()

发生了什么事?

不要用 shutil 清理这些。 tempfile.TemporaryDirectory class 提供了一个 cleanup() 方法,如果您想选择加入显式清理,只需调用它即可。

您的代码崩溃的原因是 TemporaryDirectory class 设计为在超出范围后自行清理(引用计数为零)。但是,由于您已经手动从文件系统中删除了该目录,因此当实例随后尝试删除自身时,拆卸会失败。 "No such file or directory" 错误来自 TemporaryDirectory 自己的拆解,而不是来自您的 shutil.rmtree 行!

与上下文无关:

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 所述:

On completion of the context or destruction of the temporary directory object the newly created temporary directory and all its contents are removed from the filesystem.

取消注释下面代码段中的 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()

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