Для чего хорош дескриптор уровня tempfile.mkstemp? - PullRequest
0 голосов
/ 25 марта 2020

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

Одна часть документации, которую я до сих пор игнорировал, это

mkstemp () возвращает кортеж, содержащий дескриптор уровня ОС, в открытый файл (как было бы возвращено функцией os.open ()) и абсолютный путь к этому файлу в указанном порядке.

Что такое ОС ручку высокого уровня и как ее использовать?

Фон

Я всегда использовал это так:

from tempfile import mstemp

_, path = mkstemp(prefix=prefix, suffix=suffix, dir=dir)

with open(path, "w") as f:
    f.write(data)

# do something

os.remove(path)

До сих пор все работало нормально. Однако сегодня я написал небольшой скрипт, который генерирует огромные файлы и удаляет их. Скрипт прервал выполнение с сообщением

OSError: [Errno 28] No space left on device

Когда я проверил, было 80 ГБ свободно.

Я подозреваю, что os.remove только "помечал" файлы для удаления, но файлы не были удалены должным образом. И следующим подозрением было то, что мне может потребоваться закрыть дескриптор уровня ОС, прежде чем ОС действительно сможет освободить это дисковое пространство.

1 Ответ

1 голос
/ 26 марта 2020

Ваше подозрение верно. os.remove удаляет только запись каталога, содержащую имя файла. Однако данные файла остаются нетронутыми и продолжают занимать место на диске, пока не будет закрыт последний открытый дескриптор файла. В течение этого времени обычные операции над файлом через существующие дескрипторы продолжают работать, что означает, что вы все равно можете использовать дескриптор _ для поиска, чтения или записи в файл после возврата os.remove.

На самом деле это обычная практика - немедленно os.remove файл, прежде чем переходить к использованию дескриптора для работы с содержимым файла. Это предотвращает открытие файла любым другим процессом, а также означает, что файл не будет зависать, если эта программа неожиданно умирает до достижения более позднего os.remove.

Конечно, это работает, только если вы готовы и можете использовать низкоуровневый дескриптор для всех ваших операций с файлом, или , если вы используете метод os.fdopen для создания объекта файла поверх дескриптора и использовать этот новый объект для всех операций. Очевидно, вы хотите сделать только одну из этих вещей; смешивание доступа дескриптора и доступа к объекту-файлу к одному и тому же файлу может привести к непредвиденным результатам.

os.fdopen(_) должен выполняться быстрее, чем open(path), но у него нет интеграции с диспетчером контекста, которую имеет open, поэтому он не может использоваться напрямую в конструкции with. Я думаю, что вы можете использовать contextlib.closing, чтобы обойти это.

...