Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing NamedTemporaryFile to a subprocess on windows

The python docs for tempfile.NamedTemporaryFile say:

Whether the name can be used to open the file a second time, while the named temporary file is still open, varies across platforms (it can be so used on Unix; it cannot on Windows NT or later).

I have a program that's invoked as prog input.txt, that I can't touch. I want to write a python function to give this a string.

Here's various approaches that don't quite work:

from tempfile import NamedTemporaryFile
  1. Not obvious if legal on windows

    with NamedTemporaryFile() as f:
        f.write(contents)
        subprocess.check_call(['prog', f.name])  # legal on windows?
    
  2. Might delete the file too early

    with NamedTemporaryFile() as f:
        f.write(contents)
        f.close()  # does this delete the file?
        subprocess.check_call(['prog', f.name])
    
  3. Doesn't clean up properly

    with NamedTemporaryFile(delete=False) as f:
        f.write(contents)  # if this fails, we never clean up!
    try:
        subprocess.check_call(['prog', f.name])
    finally:
        os.unlink(f.name)
    
  4. A little ugly

    f = NamedTemporaryFile(delete=False)
    try:
        with f:
            f.write(contents)
        subprocess.check_call(['prog', f.name])
    finally:
        os.unlink(f.name)
    

Which of these are correct?

like image 932
Eric Avatar asked Sep 29 '17 22:09

Eric


1 Answers

As you suspected, the first three variants are all broken - the first one will throw a PermissionError on Windows; the second one does indeed delete the file too early; and the third one doesn't handle exceptions properly.

Your fourth snippet is therefore the correct way to do it.


However, as you said, it is a little ugly. I suggest wrapping it in a function to improve readability and reusability:

import os
from contextlib import contextmanager
from tempfile import NamedTemporaryFile

@contextmanager
def ClosedNamedTempfile(contents, mode='w'):
    f = NamedTemporaryFile(delete=False, mode=mode)
    try:
        with f:
            f.write(contents)
        yield f.name
    finally:
        os.unlink(f.name)

Which allows us to use it like so:

with ClosedNamedTempfile('foobar\n') as f:
    subprocess.check_call(['prog', f])
like image 156
Aran-Fey Avatar answered Nov 01 '22 06:11

Aran-Fey