Как создать именованный временный файл в Windows на Python?

У меня есть программа Python, которая должна создать именованный временный файл, который будет открыт и закрыт пару раз в течение программы и должен быть удален, когда программа выйдет. К сожалению, ни один из вариантов в tempfile не работает:

  • TemporaryFile не имеет видимого имени
  • NamedTemporaryFile создает файл-подобный объект. Мне просто нужно имя файла. Я попытался закрыть объект, который он возвращает (после установки delete = False), но я получаю потоковые ошибки, когда пытаюсь открыть файл позже.
  • SpooledTemporaryFile не имеет видимого имени
  • mkstemp возвращает объект открытого файла и имя; он не гарантирует, что файл будет удален, когда программа выйдет из
  • mktemp возвращает имя файла, но не гарантирует, что файл будет удален при выходе из программы

Я попытался использовать mktemp 1 в контекстном менеджере, например:

def get_temp_file(suffix):
    class TempFile(object):
        def __init__(self):
            self.name = tempfile.mktemp(suffix = '.test')

        def __enter__(self):
            return self

        def __exit__(self, ex_type, ex_value, ex_tb):
            if os.path.exists(self.name):
                try:
                    os.remove(self.name)
                except:
                    print sys.exc_info()

     return TempFile()

... но это дает мне WindowsError(32, 'The process cannot access the file because it is being used by another process'). Имя файла используется процессом, который запускается моей программой, и даже если я гарантирую, что процесс завершится до моего выхода, он, похоже, имеет состояние гонки вне моего контроля.

Какой лучший способ справиться с этим?

1 Мне не нужно беспокоиться о безопасности здесь; это часть модуля тестирования, так что большинство людей, которые могут быть вредны, могут привести к ложным ошибкам наших модульных тестов. Ужас!

Ответ 1

Мне нужно что-то похожее на это сегодня, и в итоге я написал свой собственный. Я использую atexit.register(), чтобы зарегистрировать обратный вызов функции, который удаляет файл при выходе из программы.

Обратите внимание, что стандарты кодирования для этого немного отличаются от стандартных стандартов кодирования Python (camelCase, а не use_underscores). Разумеется, отрегулируйте по желанию.

def temporaryFilename(prefix=None, suffix='tmp', dir=None, text=False, removeOnExit=True):
    """Returns a temporary filename that, like mkstemp(3), will be secure in
    its creation.  The file will be closed immediately after it created, so
    you are expected to open it afterwards to do what you wish.  The file
    will be removed on exit unless you pass removeOnExit=False.  (You'd think
    that amongst the myriad of methods in the tempfile module, there'd be
    something like this, right?  Nope.)"""

    if prefix is None:
        prefix = "%s_%d_" % (os.path.basename(sys.argv[0]), os.getpid())

    (fileHandle, path) = tempfile.mkstemp(prefix=prefix, suffix=suffix, dir=dir, text=text)
    os.close(fileHandle)

    def removeFile(path):
        os.remove(path)
        logging.debug('temporaryFilename: rm -f %s' % path)

    if removeOnExit:
        atexit.register(removeFile, path)

    return path

Супер-базовый тестовый код:

path = temporaryFilename(suffix='.log')
print path
writeFileObject = open(path, 'w')
print >> writeFileObject, 'yay!'
writeFileObject.close()

readFileObject = open(path, 'r')
print readFileObject.readlines()
readFileObject.close()

Ответ 2

У меня была точно такая же проблема, когда мне нужно было сохранить загруженный файл в открытый временный файл с помощью модуля csv. Наиболее раздражающим было то, что имя файла в WindowsError указывало на временный файл, но сохранение содержимого загружаемого файла в буфер StringIO и толкание данных буфера во временный файл устраняло проблему. Для моих потребностей этого было достаточно, поскольку загруженные файлы всегда вписывались в память.

Проблема заключалась только в том, что я загрузил файл с помощью script через Apache CGI, когда я запускал аналогичный script с консоли, но не смог воспроизвести проблему.

Ответ 3

Если вы не заботитесь о безопасности, что с этим не так?

tmpfile_name = tempfile.mktemp()
# do stuff
os.unlink(tmpfile_name)

Возможно, вы пытаетесь переусердствовать. Если вы хотите, чтобы этот файл всегда удалялся, когда программа завершается, вы можете обернуть выполнение main() в try/finally. Держите это просто!

if __name__ == '__main__':
    try:
        tmpfile_name = tempfile.mktemp()
        main()
    except Whatever:
        # handle uncaught exception from main()
    finally:
        # remove temp file before exiting
        os.unlink(tmpfile_name)

Ответ 4

Как насчет создания временного каталога, а затем статического имени файла в этом каталоге? Каталог и файл удаляются при выходе из контекста.

with tempfile.TemporaryDirectory() as directory_name:
  filename = os.path.join(directory_name, 'file' + suffix)
  # use the filename to open a file for writing, or run a os.system() command, etc.