tempfile.TemporaryDirectory contextmanager 不使用 /tmp 文件夹

tempfile.TemporaryDirectory contextmanager does not use /tmp folder

我使用 tempfile.TemporaryDirectory class 作为上下文管理器。它应该默认使用 /tmp 文件夹。我尝试过使用默认值,也尝试过强制使用 /tmp 文件夹。它会在调用者脚本所在的位置创建临时文件夹。

设置:

代码:

import tempfile
import os

print(os.path.dirname(__file__))
TMP_DIR_PREFIX = "my_test_"
with tempfile.TemporaryDirectory(prefix=TMP_DIR_PREFIX, dir="/tmp") as tmp_dir:
       print(tmp_dir)

输出:

>>> python3 /home/my_home/test.py 
home/my_home
home/my_home/my_test_m1vljq2h

我的问题:

我已经阅读了相关的官方文档以及tempfile模块的实现,但我没有找到任何相关代码部分可以导致此类问题。

注意: 如果可能的话,我不想继承和更改此模块的许多元素,但我愿意接受想法。

编辑:

完整回溯(来自 Jenkins 运行):

File "/my_path/python/3.6.0/lib/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "/my_path/python/3.6.0/lib/python3.6/threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "/home/my_home/copy_to_location.py", line 143, in upload_files
    with tempfile.TemporaryDirectory(prefix=TMP_DIR_PREFIX, dir="/tmp") as tmp_dir:
  File "/my_path/python/3.6.0/lib/python3.6/tempfile.py", line 790, in __init__
    self.name = mkdtemp(suffix, prefix, dir)
  File "/my_path/python/3.6.0/lib/python3.6/tempfile.py", line 368, in mkdtemp
    _os.mkdir(file, 0o700)
PermissionError: [Errno 13] Permission denied: '/home/my_home/my_test_q1pldmf2'

编辑2:

我无法在本地复制它。此问题仅在 Jenkins 中经常发生!

编辑3:

添加行:

logging.info(tempfile._sanitize_params("my_test_", None, None))

Jenkins 中的输出:

2019-11-08 15:09:26 [Thread-1] [INFO] ('my_test_', '', '/tmp', <class 'str'>)

更改行:

logging.info(tempfile._sanitize_params("my_test_", None, "/tmp"))

在詹金斯中添加:

2019-11-08 15:13:46 [Thread-1] [INFO] ('my_test_', '', '/tmp', <class 'str'>)

如果您不给它一个 dir 参数,tempfile.TemporaryDirectory() 对象将使用 tempfile.mkdtemp() to create the temporary directory from the arguments passed in. This, in turn, will use tempfile.gettempdir()

如果你传入一个dir='/tmp'并且仍然看不到/tmp中创建的目录,那么有两种可能:

  • 您的 prefix 值不是您认为的那样,而是以 /
  • 开头
  • 您系统 上的 tempfile 模块 已被更改,其行为方式不再与 standard library version distributed with Python 3.6.0 相同。这些更改可以在磁盘上进行,也可以通过其他 Python 动态更改行为的代码进行。

正常行为是 mkdtemp() 函数调用一个名为 _sanitize_params() 的内部函数,returns dir 如果已设置则不变,并且值gettempdir()否则:

>>> import tempfile
>>> tempfile._sanitize_params('my_test_', None, '/tmp')
('my_test_', '', '/tmp', <class 'str'>)
>>> tempfile._sanitize_params('my_test_', None, None)
('my_test_', '', '/tmp', <class 'str'>)
>>> tempfile.gettempdir()
'/tmp'

mkdtemp() 然后使用该调用的结果(返回更新的 prefixsuffixdir 以及 bytesstr type) 和随机字符串,为您创建一个新目录。

这导致您可能没有正确排除 prefix 值确实是您认为的那样。 mkdtemp() 函数使用:

os.path.join(dir, prefix + name + suffix)

将前缀、候选 name(随机值)和后缀(在您的情况下为空字符串)串联起来加入 dir 路径。但请注意,os.path.join() function 丢弃以 / 斜杠 :

开头的参数之前的所有路径元素
>>> import os.path
>>> os.path.join("/foo", "bar")
'/foo/bar'
>>> os.path.join("/foo", "/bar")
'/bar'

因此,您看到的行为也可以通过以斜线开头的前缀来解释,因此:

TMP_DIR_PREFIX = "/home/my_home/my_test_"

会立即产生相同的结果:

>>> TMP_DIR_PREFIX = "/home/my_home/my_test_"
>>> tempfile.mkdtemp(prefix=TMP_DIR_PREFIX, dir="/tmp")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/.../lib/python3.6/tempfile.py", line 368, in mkdtemp
    _os.mkdir(file, 0o700)
PermissionError: [Errno 13] Permission denied: '/home/my_home/my_test_v4cqpamm'

之前已将此报告给 Python 项目 issue #35278

您可以在 Jenkins 作业中简单地包含两个测试来排除这些选项。确保记录 TMP_DIR_PREFIX 值,以及 tempfile._sanitize_params(TMP_DIR_PREFIX, None, '/tmp') returns.

如果其中任何一个都没有在您的系统上产生预期的输出,那么您就知道您需要专注于寻找; tempfile 模块行为已更改,或者您假设 TMP_DIR_PREFIX 具有它所具有的值是不正确的。

您可以使用以下 shell 命令检查本地副本是否与发布版本不同:

$ diff -u \
> <(curl -s https://raw.githubusercontent.com/python/cpython/v3.6.0/Lib/tempfile.py) \
> /my_path/python/3.6.0/lib/python3.6/tempfile.py

或者您可以计算文件的校验和:

import hashlib
with open(tempfile.__file__, 'rb') as file_to_hash:
    tempfile_checksum = hashlib.sha1(file_to_hash.read()).hexdigest()

并将该校验和值与已发布文件的校验和值进行比较:

$ curl -s https://raw.githubusercontent.com/python/cpython/v3.6.0/Lib/tempfile.py | \
> sha1sum
38ad01ccc5972e193e1b96a1de8b7ba1bd8d289d  -

如果没有出现任何问题,您将使用调试器单步执行调用或查看所涉及函数的 __module__ 属性。例如。例如,如果 _sanitize_params() 被动态更改(monkey patched),那么 tempfile._sanitize_params.__module__ 将不会设置为 'tempfile'。但是,请注意,您的回溯已经显示 TemporaryDirectory.__init__mkdtemp 均取自正确的文件,并且可见的两行的行号与已发布源中的行号相匹配。