22

I know that it is possible to create a temporary file, and write the data of the file I wish to copy to it. I was just wondering if there was a function like:

create_temporary_copy(file_path)
Gideon
  • 1,203
  • 1
  • 11
  • 13
  • What operating system are you using? Do you need something cross-platform? – Steven Rumbalski Jul 05 '11 at 19:03
  • yah, i need something cross-platform. I found a question under "Related" that answers my question. `temp_path = tempfile.mktemp()` `shutil.copyfile(file_to_be_copied, temp_path)` – Gideon Jul 05 '11 at 19:09

7 Answers7

29

There isn't one directly, but you can use a combination of tempfile and shutil.copy2 to achieve the same result:

import tempfile, shutil, os
def create_temporary_copy(path):
    temp_dir = tempfile.gettempdir()
    temp_path = os.path.join(temp_dir, 'temp_file_name')
    shutil.copy2(path, temp_path)
    return temp_path

You'll need to deal with removing the temporary file in the caller, though.

TorelTwiddler
  • 5,996
  • 2
  • 32
  • 39
  • 3
    or replace the first two lines in the function with: `fd, temp_path = tempfile.mkstemp()`. – user2340939 Jan 30 '19 at 11:25
  • 2
    It does not work on windows. It raises permission denied error. – milembar Jun 13 '22 at 05:34
  • 1
    I would use `temp_dir = tempfile.TemporaryDirectory()` instead. That way you can call `temp_dir.close()` when you are done, and the folder will automatically be deleted. Or even better, use `with tempfile.TemporaryDirectory() as temp_dir:` – Ido Jan 26 '23 at 09:54
14

This isn't quite as concise, and I imagine there may be issues with exception safety, (e.g. what happens if 'original_path' doesn't exist, or the temporary_copy object goes out of scope while you have the file open) but this code adds a little RAII to the clean up. The difference here to using NamedTemporaryFile directly is that rather than ending up with a file object, you end up with a file, which is occasionally desirable (e.g. if you plan to call out to other code to read it, or some such.)

import os,shutil,tempfile
class temporary_copy(object):

    def __init__(self,original_path):
        self.original_path = original_path

    def __enter__(self):
        temp_dir = tempfile.gettempdir()
        base_path = os.path.basename(self.original_path)
        self.path = os.path.join(temp_dir,base_path)
        shutil.copy2(self.original_path, self.path)
        return self.path

    def __exit__(self,exc_type, exc_val, exc_tb):
        os.remove(self.path)

in your code you'd write:

with temporary_copy(path) as temporary_path_to_copy:
    ... do stuff with temporary_path_to_copy ...

# Here in the code, the copy should now have been deleted.
user293026
  • 157
  • 1
  • 4
  • How to do the same, but also return the opened file descriptor, while closing it in the end before deletion? – Gulzar Mar 06 '23 at 15:17
  • @Gulzar one way is to nest with statements to just open the file after it's created. If you want a class that does it all, you can create a an object attribute in the `__enter__()` method to store the open file descriptor and then call `close()` on the attribute in the `__exit__()` method. If you want to use inheritance, then you can create a class that inherits from the `temporary_copy` class and then calls its `__enter__()` to get the path and then stores/returns the file descriptor in its own `__enter__()`. – JeffP Mar 28 '23 at 23:36
7

The following is more concise (OP's ask) than the selected answer. Enjoy!

import tempfile, shutil, os
def create_temporary_copy(path):
  tmp = tempfile.NamedTemporaryFile(delete=True)
  shutil.copy2(path, tmp.name)
  return tmp.name
David Bernat
  • 324
  • 2
  • 11
4

A variation on @tramdas's answer, accounting for the fact that the file cannot be opened twice on windows. This version ignores the preservation of the file extension.

import os, shutil, tempfile

def create_temporary_copy(src):
  # create the temporary file in read/write mode (r+)
  tf = tempfile.TemporaryFile(mode='r+b', prefix='__', suffix='.tmp')

  # on windows, we can't open the the file again, either manually
  # or indirectly via shutil.copy2, but we *can* copy
  # the file directly using file-like objects, which is what
  # TemporaryFile returns to us.
  # Use `with open` here to automatically close the source file
  with open(src,'r+b') as f:
    shutil.copyfileobj(f,tf)

  # display the name of the temporary file for diagnostic purposes
  print 'temp file:',tf.name

  # rewind the temporary file, otherwise things will go
  # tragically wrong on Windows
  tf.seek(0) 
  return tf

# make a temporary copy of the file 'foo.txt'
name = None

with create_temporary_copy('foo.txt') as temp:
  name = temp.name

  # prove that it exists
  print 'exists', os.path.isfile(name) # prints True

  # read all lines from the file
  i = 0
  for line in temp:
    print i,line.strip()
    i += 1

  # temp.close() is implicit using `with`

# prove that it has been deleted
print 'exists', os.path.isfile(name) # prints False
cod3monk3y
  • 9,508
  • 6
  • 39
  • 54
0

A slight variation (in particular I needed the preserve_extension feature for my use case, and I like the "self-cleanup" feature):

import os, shutil, tempfile
def create_temporary_copy(src_file_name, preserve_extension=False):
    '''
    Copies the source file into a temporary file.
    Returns a _TemporaryFileWrapper, whose destructor deletes the temp file
    (i.e. the temp file is deleted when the object goes out of scope).
    '''
    tf_suffix=''
    if preserve_extension:
        _, tf_suffix = os.path.splitext(src_file_name)
    tf = tempfile.NamedTemporaryFile(suffix=tf_suffix)
    shutil.copy2(src_file_name, tf.name)
    return tf
tramdas
  • 448
  • 2
  • 10
  • 2
    The line `shutil.copy2()` gives me `Permission denied` error (at least, on Windows) – cod3monk3y Nov 13 '14 at 20:12
  • Sorry, I don't have access to a Windows machine to test (I'm on OSX). Could be that the default path used by `NamedTemporaryFile` isn't sensible on Windows; could you print out `tf.name` before the `shutil.copy2` to see where it's creating the file? – tramdas Nov 14 '14 at 04:28
  • 1
    `IOError: [Errno 13] Permission denied: 'c:\\users\\MYNAME\\appdata\\local\\temp\\tmpt7m7ix.tmp'`. I tested on Linux and it works fine. I'm assuming `NamedTemporaryFile` is opening an exclusive handle on the temporary file on Windows. I have write access to that directory and can manually create files just fine, or create a file in the temporary directory as listed in @TorelTwiddler's response. – cod3monk3y Nov 14 '14 at 15:50
  • 1
    According to [this post](http://stackoverflow.com/a/23212515/1174169) (and the docs), the file handle opened by `TemporaryFile` cannot be re-opened on Windows NT and later. `shutil.copy2` attempts to open the file again for a copy, thus the error. – cod3monk3y Nov 14 '14 at 16:07
  • I posted a variation that works on windows using `shutil.copyfileobj`, accounting for these differences. – cod3monk3y Nov 14 '14 at 17:18
0

You can try this to create a temporary file and then a copy. I like to set delete=False in case I need to do something to the temp file before creating the copy. The temp file is then closed manually and thus deleted:

import shutil, tempfile, os
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False)
#Do something to temp file
shutil.copyfile('copy_name.txt', temp_file.name)
temp_file.close()
frankfnl
  • 11
  • 5
0

I wanted a more generic context manager which can copy files to the temp dir. In case this helps anyone with this issue:

from contextlib import contextmanager
from pathlib import Path
from shutil import copytree
from tempfile import TemporaryDirectory
from typing import Iterator

@contextmanager
def temporary_directory(source_dir: Path | None = None) -> Iterator[Path]:
    """Create a tempory directory for use within this context, yield its path, and purge on exit.

    Args:
        source_dir (Path | None, optional): A path to a directory to copy to the temporary
            directory. Defaults to None.

    Raises:
        FileNotFoundError: Raised if source_dir is passed but does not exist or was not found.
        ValueError: Raised if source_dir is passed but is to a file, not a directory.

    Yields:
        Iterator[Path]: The path to the temporary directory with the source directory
            files within it.
    """
    if source_dir is not None and not source_dir.exists():
        raise FileNotFoundError(f"{source_dir!s} does not exist")
    if source_dir is not None and not source_dir.is_dir():
        raise ValueError(f"{source_dir!s} is not a directory")
    with TemporaryDirectory() as temp_dir:
        if source_dir is not None:
            copytree(source_dir, temp_dir, dirs_exist_ok=True)
        yield Path(temp_dir).absolute()