tempfile.TeventDirectory contextmanager не использует папку / tmp - PullRequest
0 голосов
/ 08 ноября 2019

Я использую класс tempfile.TemporaryDirectory в качестве контекстного менеджера. Он должен использовать папку /tmp по умолчанию. Я пытался использовать значение по умолчанию, а также пытался принудительно использовать папку /tmp. Он создает временную папку, в которой находится скрипт вызывающего абонента.

Настройка:

  • Red Hat 7 Linux
  • Python 3.6. 6
  • Сценарий вызывается Дженкинсом

Код:

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, но я не сделалне найти ни одной связанной части кода, которая может вызвать этот тип проблемы.

ПРИМЕЧАНИЕ: Если это возможно, я не хочу наследовать и изменять многие элементы этого модуля, но я открыт для идей.

РЕДАКТИРОВАТЬ:

Полный возврат (от запуска Дженкинса):

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'

EDIT2:

Я не могу воспроизвести его на местном языке. Эта проблема возникает только в Jenkins постоянно!

EDIT3:

Добавлена ​​строка:

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

1 Ответ

1 голос
/ 08 ноября 2019

Объект tempfile.TemporaryDirectory() использует tempfile.mkdtemp() для создания временного каталога из переданных аргументов. Это, в свою очередь, будет использовать tempfile.gettempdir(), если вы этого не сделаетепередайте ему аргумент dir.

Если вы передаете dir='/tmp' и все еще не видите каталог, созданный в /tmp, то есть две возможности:

  • ваше prefix значение не то, что вы думаете, а скорее начинается с /
  • tempfile модуля в вашей системе имеетбыл изменен и больше не работает так же, как стандартная версия библиотеки , распространяемая с Python 3.6.0 . Изменения могли быть сделаны на диске или с помощью другого кода Python, который динамически изменял поведение.

Обычное поведение для функции mkdtemp() - вызывать внутреннюю функцию с именем * 1032. *, который возвращает 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() затем использует результаты этого вызова (возвращая обновленный prefix, suffix, dir и тип bytes или str) вместе со случайными строками, чтобы создать для вас новый каталог.

Это приводит к тому, что у вас нетПравильно исключил, что значение prefix действительно то, что вы думаете. Функция mkdtemp() использует:

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

, чтобы соединить путь dir с конкатенацией вашего префикса, кандидата name (случайное значение) и суффикса (пустая строка в вашем случае). Но обратите внимание, что os.path.join() функция будет отбрасывать любые элементы пути, которые предшествуют аргументу, начинающемуся с / косой черты :

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

Об этом ранее сообщалось проекту Pythonas issue # 35278 .

Вы можете тривиально включить в свою работу Jenkins два теста, чтобы исключить эти параметры. Убедитесь, что вы записали значение TMP_DIR_PREFIX, а также то, что возвращает tempfile._sanitize_params(TMP_DIR_PREFIX, None, '/tmp').

Если какой-либо из них не дает ожидаемого результата в вашей системе, то вы знаете, что вам нужно сосредоточиться на поиске;либо изменилось поведение модуля tempfile, либо вы предполагаете, что значение TMP_DIR_PREFIX имеет неверное значение.

Вы можете проверить, отличается ли локальная копия от версии для публикации, с помощью следующей команды оболочки:

$ 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() был динамически изменен ( обезьяна исправлена ​​), тогда tempfile._sanitize_params.__module__ не будет установлен на 'tempfile', например. Тем не менее, обратите внимание, что ваша трассировка уже показывает, что TemporaryDirectory.__init__ и mkdtemp взяты из правильного файла и что номера строк для двух видимых строк совпадают с номерами в опубликованном источнике.

...