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

blu*_*onk 3 python shutil temporary-files python-3.x python-unittest

考虑这个测试

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()
Run Code Online (Sandbox Code Playgroud)

tearDown但引发错误

FileNotFoundError: [Errno 2] No such file or directory: '/tmp/tmpxz7ts7a7'
Run Code Online (Sandbox Code Playgroud)

指的是self.test_dir.name.

根据源代码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))
Run Code Online (Sandbox Code Playgroud)

而且我没有在上下文中使用它,所以__exit__()不应该根据我的理解调用它.

怎么了?

wim*_*wim 6

不要用它来清理这些shutil.本tempfile.TemporaryDirectory类提供了一个cleanup()方法,只需调用,如果你想选择加入一个明确的清理.

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


Jea*_*bre 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))
Run Code Online (Sandbox Code Playgroud)

它打印

True
False
Run Code Online (Sandbox Code Playgroud)

因此,一旦t设置为None对象的引用被垃圾收集并且目录被删除,正如文档所述:

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

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)
Run Code Online (Sandbox Code Playgroud)

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

调用cleanup()对象方法而不是rmtree解决问题,因为更新了对象内部状态,以便在最终确定时尝试删除目录(如果你问我,对象应该在尝试清理之前测试目录是否存在,但即便如此因为它不是原子操作所以总是有效

所以更换

shutil.rmtree(self.test_dir.name)
Run Code Online (Sandbox Code Playgroud)

通过

self.test_dir.cleanup()
Run Code Online (Sandbox Code Playgroud)

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