A context with temporary filename

Gribouillis 1 Tallied Votes 288 Views Share

This snippet defines a context autofilename() to create an existing temporary file with a new name in python. Unlike the methods in the tempfile module, it only gives a filename which exists within a 'with' block, instead of an open file. This is useful for example when a program needs to pass a new filename to a function which does not accept a file object argument. As with the tempfile module, there should be no race condition between processes on the temporary file creation.

# python >= 2.5
from __future__ import with_statement
from contextlib import contextmanager
from tempfile import mkstemp
import os

@contextmanager
def autofilename(suffix='', prefix='tmp', dir=None, delete=True):
    """This context creates a visible temporary file with size 0 in the file system.
    This file is removed by default when the context exits (delete = True).
    Unlike the tempfile module, this context only offers a filename (and not an open file) to
    the code that uses it.
    """
    fd, name = mkstemp(suffix=suffix, prefix=prefix, dir=dir)
    try:
        os.close(fd)
        yield name
    finally:
        if delete:
            try:
                os.remove(name)
            except OSError:
                pass
            
# USE CASE

if __name__ == "__main__":
    def report(filename):
        print("file '%s' exists: %s" % (filename, "yes." if os.path.exists(name) else "no."))

    with autofilename() as name:
        report(name) # check that a file with that name exists in the system
        # use the filename temporarily
        with open(name, "w") as f_out:
            f_out.write("hello world")
        with open(name, "r") as f_in:
            print(f_in.read())
            
    # after the with block, the file was deleted in the system
    report(name)
    
""" my output -->
file '/tmp/tmpAzHfIz' exists: yes.
hello world
file '/tmp/tmpAzHfIz' exists: no.
"""
TrustyTony 888 pyMod Team Colleague Featured Poster

This is useful for example when a program needs to pass a new filename to a function which does not accept a file object argument.

You can also pass .name of the file object but then you should close the file by hand before function reopens it from the name.

import sys

def takes_filename(fn):
    print('Name of file is', fn)
    
myfile = open(sys.argv[0])
myfile.close()
takes_filename(myfile.name)
assert myfile.name == sys.argv[0]
Gribouillis 1,391 Programming Explorer Team Colleague

You can also pass .name of the file object but then you should close the file by hand before function reopens it from the name.

import sys

def takes_filename(fn):
    print('Name of file is', fn)
    
myfile = open(sys.argv[0])
myfile.close()
takes_filename(myfile.name)
assert myfile.name == sys.argv[0]

Yes, to be more precise, I had this idea after this discussion http://www.daniweb.com/software-development/python/threads/363125 which used a temporary postscript file. Here is how one could write a function to save a tkinter canvas

def save_image(tk_canvas, image_filename):
    with autofilename(suffix=".eps") as name: # use a temporary .eps file
        tk_canvas.postscript(file=name)
        subprocess.Popen("convert {0} {1}".format(name, image_filename), shell=True).wait()

Here, the context permits to create a temporary postscript file on the fly without worrying about its lifetime.

Gribouillis 1,391 Programming Explorer Team Colleague

In a similar vein, here is a context to create an automatically deleted temporary directory

@contextmanager
def autodirname(suffix='', prefix='tmp', dir=None, delete=True, ignore_errors=False, onerror=None):
    """This context creates a visible temporary directory in the file system.
    This directory is removed by default when the context exits (delete = True).
    The parameters ignore_error and onerror have the same meaning as in shutil.rmtree()
    """
    from tempfile import mkdtemp
    name = mkdtemp(suffix=suffix, prefix=prefix, dir=dir)
    try:
        yield name
    finally:
        if delete:
            from shutil import rmtree
            rmtree(name, ignore_errors) if onerror is None else rmtree(name, ignore_errors, onerror)
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.