python - tempfile.TemporaryDirectory 上下文管理器不使用/tmp 文件夹

标签 python python-3.x linux python-3.6 redhat

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

设置:

  • 红帽 7 Linux
  • python 3.6.6
  • 脚本是从 Jenkins 调用的

代码:

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

我的问题:

  • 有人知道我该如何解决吗?

  • 这怎么可能?我已经检查了 TemporaryDirectory 类的实现,但我看不出任何原因。如果有人能够解释其中的原因,我将非常高兴。

我已经阅读了相关的官方文档以及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"))

在 Jenkins 中添加:

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

最佳答案

tempfile.TemporaryDirectory() 对象使用 tempfile.mkdtemp()从传入的参数创建临时目录。这反过来将使用 tempfile.gettempdir()如果你不给它一个 dir 参数。

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

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

mkdtemp() 函数的正常行为是调用名为 _sanitize_params() 的内部函数, 如果设置了 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() 然后使用该调用的结果(返回更新的 prefixsuffixdirbytesstr 类型)和随机字符串,为您创建一个新目录。

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

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

dir 路径与前缀、候选 name(随机值)和后缀(在您的情况下为空字符串)串联起来。但请注意 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'

这在之前以 issue #35278 的形式报告给 Python 项目.

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

如果其中任何一个都没有在您的系统上产生预期的输出,那么您就知道您需要专注于寻找; 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 都取自正确的文件,并且可见的两行的行号与已发布的行号相匹配来源。

关于python - tempfile.TemporaryDirectory 上下文管理器不使用/tmp 文件夹,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58767241/

相关文章:

c++ - c++是否允许在任何函数之外调用函数?

python - 如何将数据发送到 wave.writeframesraw(data)

python - matplotlib.pyplot 错误 "ImportError: cannot import name ' _path'"

python - 在 Django 的 admin change_form 中创建自定义按钮

python-3.x - 按下 "Ctrl+C"时 python 回显服务器的 Unicode 错误

linux - 'git clone' 遵循 umask,顶级项目目录除外

c - fork 子进程不断被终止,状态为 0x008B

python - 导入 .pyd 文件时出现 UnicodeDecodeError

python - 在一个线程中创建的对象只能在同一个线程中使用

python - 如何使用循环 Pandas 将值添加到列