Как создать именованный временный файл в 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 Здесь мне не нужно беспокоиться о безопасности; это часть модуля тестирования, поэтому самое большее, что может сделать кто-то нечестивый, - это вызвать ложный сбой наших модульных тестов. Ужас!


person Chris B.    schedule 30.03.2010    source источник
comment
Проблемы безопасности не влияют на программу, использующую временный файл, они влияют на всю систему, в которой создается временный файл (например, создание символьной ссылки на важный файл после его создания).   -  person Ignacio Vazquez-Abrams    schedule 31.03.2010
comment
Вы уверены, что все файловые объекты, открывающие временный файл, закрыты правильно?   -  person user49117    schedule 31.03.2010
comment
Имя файла передается в процесс Windows через Popen (), и когда я закончу с ним, я вызываю .flush () в потоках .sysin, .sysout и .syserr, а затем вызываю .wait () в процессе. . Это завернуто в ContextManager, поэтому я уверен, что он вызывается. Я не уверен, что еще я мог делать.   -  person Chris B.    schedule 31.03.2010
comment
Перед звонит unlink. А именно handle, temp_file = tempfile.mkstemp(). Тогда твой код. Наконец, os.close(handle) и os.unlink(temp_file).   -  person KitKat    schedule 22.04.2014


Ответы (4)


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

Обратите внимание, что стандарты кодирования для этого немного отличаются от типичных стандартов кодирования Python (camelCase, а не using_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's 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()
person André Pang    schedule 24.03.2011

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

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

person newtover    schedule 02.04.2010

Если вас не волнует безопасность, что в этом плохого?

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)
person jathanism    schedule 31.03.2010
comment
Согласно документации, os.unlink совпадает с os.remove. Если это так, я должен получить такое же исключение, верно? - person Chris B.; 31.03.2010

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

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.
person Hugues    schedule 04.09.2019