Chris B.
Chris B.

Reputation: 90211

How do I create a named temporary file on windows in Python?

I've got a Python program that needs to create a named temporary file which will be opened and closed a couple times over the course of the program, and should be deleted when the program exits. Unfortunately, none of the options in tempfile seem to work:

I've tried using mktemp1 within a context manager, like so:

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

... but that gives me a WindowsError(32, 'The process cannot access the file because it is being used by another process'). The filename is used by a process my program spawns, and even though I ensure that process finishes before I exit, it seems to have a race condition out of my control.

What's the best way of dealing with this?

1 I don't need to worry about security here; this is part of a testing module, so the most someone nefarious could do is cause our unit tests to spuriously fail. The horror!

Upvotes: 15

Views: 9610

Answers (5)

Hunter_71
Hunter_71

Reputation: 789

I have a workaround for this:

import os
from contextlib import contextmanager
from tempfile import TemporaryFile
from typing import Generator, IO


@contextmanager
def temp_file() -> Generator[IO, None, None]:
    """
    This is a workaround for creating temporary files on Windows,
    as regular `with TemporaryFile() as tmp` not working properly.
    """
    tmp = TemporaryFile(delete=False)
    try:
        yield tmp
        tmp.close()
    finally:
        # remove temp file before exiting
        os.unlink(tmp.name)

usage:

with temp_file() as tmp:
   # do some stuff

Upvotes: 2

Hugues
Hugues

Reputation: 3170

How about creating a temporary directory and then a static filename within that directory? The directory and the file are removed upon exit from the context.

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.

Upvotes: 3

André Pang
André Pang

Reputation: 51

I needed something similar to this today, and ended up writing my own. I'm using atexit.register() to register a function callback that removes the file when the program exits.

Note that the coding standards for this are slightly different from the typical Python coding standards (camelCase rather than using_underscores). Adjust at will, of course.

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

Super-basic test code:

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

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

Upvotes: 5

newtover
newtover

Reputation: 32094

I have had exactly the same problem when I needed to save an uploaded file to the opened temporary file using the csv module. The most irritating thing was that the filename in WindowsError pointed to the temporary file, but saving the uploading file contents into the StringIO buffer and pushing the buffer data into the temporary file fixed the problem. For my needs that was enough since uploaded files always fit in memory.

The problem was only when I uploaded a file with a script through Apache's CGI, when I ran the similar script from console I could not reproduce the problem though.

Upvotes: 2

jathanism
jathanism

Reputation: 33716

If you don't care about the security what is wrong with this?

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

You might be trying to over-engineer this. If you want to make sure that this file is always removed when the program exits, you could wrap your main() execution in a try/finally. Keep it simple!

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)

Upvotes: 2

Related Questions