diff -r 54f6d8c4dfaf Doc/library/tempfile.rst --- a/Doc/library/tempfile.rst Sat Apr 06 20:31:26 2013 -0500 +++ b/Doc/library/tempfile.rst Tue Apr 16 23:47:09 2013 -0400 @@ -31,7 +31,7 @@ The module defines the following user-callable items: -.. function:: TemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix='', prefix='tmp', dir=None) +.. function:: TemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix='', prefix='tmp', dir=None, copy_from=None) Return a :term:`file-like object` that can be used as a temporary storage area. The file is created using :func:`mkstemp`. It will be destroyed as soon @@ -47,7 +47,7 @@ stored. *buffering*, *encoding* and *newline* are interpreted as for :func:`open`. - The *dir*, *prefix* and *suffix* parameters are passed to :func:`mkstemp`. + The *dir*, *prefix*, *suffix*, and *copy_from* parameters are passed to :func:`mkstemp`. The returned object is a true file object on POSIX platforms. On other platforms, it is a file-like object whose :attr:`!file` attribute is the @@ -55,7 +55,7 @@ :keyword:`with` statement, just like a normal file. -.. function:: NamedTemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix='', prefix='tmp', dir=None, delete=True) +.. function:: NamedTemporaryFile(mode='w+b', buffering=None, encoding=None, newline=None, suffix='', prefix='tmp', dir=None, delete=True, copy_from=None) This function operates exactly as :func:`TemporaryFile` does, except that the file is guaranteed to have a visible name in the file system (on @@ -92,7 +92,7 @@ the truncate method now accepts a ``size`` argument. -.. function:: TemporaryDirectory(suffix='', prefix='tmp', dir=None) +.. function:: TemporaryDirectory(suffix='', prefix='tmp', dir=None, copy_from=None) This function creates a temporary directory using :func:`mkdtemp` (the supplied arguments are passed directly to the underlying function). @@ -110,7 +110,7 @@ .. versionadded:: 3.2 -.. function:: mkstemp(suffix='', prefix='tmp', dir=None, text=False) +.. function:: mkstemp(suffix='', prefix='tmp', dir=None, text=False, copy_from=None) Creates a temporary file in the most secure manner possible. There are no race conditions in the file's creation, assuming that the platform @@ -143,6 +143,9 @@ mode (the default) or text mode. On some platforms, this makes no difference. + If *copy_from* is specified, the contents of the file represented + by the given pathname will be copied into the temporary file. + :func:`mkstemp` returns a tuple containing an OS-level handle to an open file (as would be returned by :func:`os.open`) and the absolute pathname of that file, in that order. diff -r 54f6d8c4dfaf Lib/tempfile.py --- a/Lib/tempfile.py Sat Apr 06 20:31:26 2013 -0500 +++ b/Lib/tempfile.py Tue Apr 16 23:47:09 2013 -0400 @@ -33,6 +33,7 @@ import os as _os import errno as _errno from random import Random as _Random +from shutil import copyfileobj as _copyfileobj try: import fcntl as _fcntl @@ -207,9 +208,15 @@ return _name_sequence -def _mkstemp_inner(dir, pre, suf, flags): +def _copyfile(src, dst): + """Copy function that avoids closing dst temp file, and thus deletion.""" + with open(src, 'rb') as fsrc: + fdst = open(dst, 'wb') + _copyfileobj(fsrc, fdst) + + +def _mkstemp_inner(dir, pre, suf, flags, copy_from=None): """Code common to mkstemp, TemporaryFile, and NamedTemporaryFile.""" - names = _get_candidate_names() for seq in range(TMP_MAX): @@ -218,7 +225,10 @@ try: fd = _os.open(file, flags, 0o600) _set_cloexec(fd) - return (fd, _os.path.abspath(file)) + path = _os.path.abspath(file) + if copy_from: + _copyfile(copy_from, path) + return (fd, path) except FileExistsError: continue # try again @@ -246,7 +256,7 @@ _once_lock.release() return tempdir -def mkstemp(suffix="", prefix=template, dir=None, text=False): +def mkstemp(suffix="", prefix=template, dir=None, text=False, copy_from=None): """User-callable function to create and return a unique temporary file. The return value is a pair (fd, name) where fd is the file descriptor returned by os.open, and name is the filename. @@ -264,6 +274,9 @@ mode. Else (the default) the file is opened in binary mode. On some operating systems, this makes no difference. + If *copy_from* is specified, the contents of the file represented + by the given filepath will be copied into the temporary file. + The file is readable and writable only by the creating user ID. If the operating system uses permission bits to indicate whether a file is executable, the file is executable by no one. The file @@ -280,7 +293,7 @@ else: flags = _bin_openflags - return _mkstemp_inner(dir, prefix, suffix, flags) + return _mkstemp_inner(dir, prefix, suffix, flags, copy_from) def mkdtemp(suffix="", prefix=template, dir=None): @@ -412,7 +425,7 @@ def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix="", prefix=template, - dir=None, delete=True): + dir=None, delete=True, copy_from=None): """Create and return a temporary file. Arguments: 'prefix', 'suffix', 'dir' -- as for mkstemp. @@ -421,6 +434,7 @@ 'encoding' -- the encoding argument to io.open (default None) 'newline' -- the newline argument to io.open (default None) 'delete' -- whether the file is deleted on close (default True). + 'copy_from' -- the filepath of the file to copy from (default None). The file is created as mkstemp() would do it. Returns an object with a file-like interface; the name of the file @@ -438,7 +452,7 @@ if _os.name == 'nt' and delete: flags |= _os.O_TEMPORARY - (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags) + (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags, copy_from) file = _io.open(fd, mode, buffering=buffering, newline=newline, encoding=encoding) @@ -452,7 +466,7 @@ else: def TemporaryFile(mode='w+b', buffering=-1, encoding=None, newline=None, suffix="", prefix=template, - dir=None): + dir=None, copy_from=None): """Create and return a temporary file. Arguments: 'prefix', 'suffix', 'dir' -- as for mkstemp. @@ -460,6 +474,7 @@ 'buffering' -- the buffer size argument to io.open (default -1). 'encoding' -- the encoding argument to io.open (default None) 'newline' -- the newline argument to io.open (default None) + 'copy_from' -- the filepath of the file to copy from (default None). The file is created as mkstemp() would do it. Returns an object with a file-like interface. The file has no @@ -471,7 +486,7 @@ flags = _bin_openflags - (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags) + (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags, copy_from) try: _os.unlink(name) return _io.open(fd, mode, buffering=buffering, diff -r 54f6d8c4dfaf Lib/test/test_tempfile.py --- a/Lib/test/test_tempfile.py Sat Apr 06 20:31:26 2013 -0500 +++ b/Lib/test/test_tempfile.py Tue Apr 16 23:47:09 2013 -0400 @@ -264,11 +264,11 @@ _close = os.close _unlink = os.unlink - def __init__(self, dir, pre, suf, bin): + def __init__(self, dir, pre, suf, bin, copy_from=None): if bin: flags = self._bflags else: flags = self._tflags - (self.fd, self.name) = tempfile._mkstemp_inner(dir, pre, suf, flags) + (self.fd, self.name) = tempfile._mkstemp_inner(dir, pre, suf, flags, copy_from) def write(self, str): os.write(self.fd, str) @@ -277,10 +277,10 @@ self._close(self.fd) self._unlink(self.name) - def do_create(self, dir=None, pre="", suf="", bin=1): + def do_create(self, dir=None, pre="", suf="", bin=1, copy_from=None): if dir is None: dir = tempfile.gettempdir() - file = self.mkstemped(dir, pre, suf, bin) + file = self.mkstemped(dir, pre, suf, bin, copy_from) self.nameCheck(file.name, dir, pre, suf) return file @@ -292,6 +292,8 @@ self.do_create(suf="b").write(b"blat") self.do_create(pre="a", suf="b").write(b"blat") self.do_create(pre="aa", suf=".txt").write(b"blat") + file_dir = os.path.dirname(os.path.realpath(__file__)) + self.do_create(copy_from=os.path.join(file_dir, "copy_from_test.txt")) def test_basic_many(self): # _mkstemp_inner can create many files (stochastic) @@ -372,6 +374,13 @@ os.lseek(f.fd, 0, os.SEEK_SET) self.assertEqual(os.read(f.fd, 20), b"blat") + def test_copy_from(self): + # _mkstemp_inner can create a temp file with copied content + from_f = os.path.dirname(os.path.realpath(__file__))+"/copy_from_test.txt" + to_f = self.do_create(copy_from=from_f) + with open(from_f, "rb") as copied_from: + with open(to_f.name, "rb") as copied_to: + self.assertEqual(copied_from.read(), copied_to.read()) class TestGetTempPrefix(BaseTestCase): """Test gettempprefix().""" @@ -433,10 +442,10 @@ class TestMkstemp(BaseTestCase): """Test mkstemp().""" - def do_create(self, dir=None, pre="", suf=""): + def do_create(self, dir=None, pre="", suf="", copy_from=None): if dir is None: dir = tempfile.gettempdir() - (fd, name) = tempfile.mkstemp(dir=dir, prefix=pre, suffix=suf) + (fd, name) = tempfile.mkstemp(dir=dir, prefix=pre, suffix=suf, copy_from=copy_from) (ndir, nbase) = os.path.split(name) adir = os.path.abspath(dir) self.assertEqual(adir, ndir, @@ -456,6 +465,8 @@ self.do_create(pre="a", suf="b") self.do_create(pre="aa", suf=".txt") self.do_create(dir=".") + file_dir = os.path.dirname(os.path.realpath(__file__)) + self.do_create(copy_from=os.path.join(file_dir, "copy_from_test.txt")) def test_choose_directory(self): # mkstemp can create directories in a user-selected directory @@ -591,11 +602,11 @@ class TestNamedTemporaryFile(BaseTestCase): """Test NamedTemporaryFile().""" - def do_create(self, dir=None, pre="", suf="", delete=True): + def do_create(self, dir=None, pre="", suf="", delete=True, copy_from=None): if dir is None: dir = tempfile.gettempdir() file = tempfile.NamedTemporaryFile(dir=dir, prefix=pre, suffix=suf, - delete=delete) + delete=delete, copy_from=copy_from) self.nameCheck(file.name, dir, pre, suf) return file @@ -608,6 +619,8 @@ self.do_create(suf="b") self.do_create(pre="a", suf="b") self.do_create(pre="aa", suf=".txt") + file_dir = os.path.dirname(os.path.realpath(__file__)) + self.do_create(copy_from=os.path.join(file_dir, "copy_from_test.txt")) def test_creates_named(self): # NamedTemporaryFile creates files with names @@ -661,6 +674,15 @@ pass self.assertRaises(ValueError, use_closed) + def test_copy_from(self): + # A NamedTemporaryFile can be created from another file + from_f = os.path.dirname(os.path.realpath(__file__))+"/copy_from_test.txt" + to_f = self.do_create(pre="copy_from", suf=".txt", copy_from=from_f) + with open(from_f, "rb") as copied_from: + with open(to_f.name, "rb") as copied_to: + self.assertEqual(copied_from.read(), copied_to.read()) + + # How to test the mode and bufsize parameters? @@ -925,6 +947,8 @@ # TemporaryFile can create files # No point in testing the name params - the file has no name. tempfile.TemporaryFile() + file_dir = os.path.dirname(os.path.realpath(__file__)) + tempfile.TemporaryFile(copy_from=os.path.join(file_dir, "copy_from_test.txt")) def test_has_no_name(self): # TemporaryFile creates files with no names (on this system) @@ -964,6 +988,13 @@ roundtrip("\u039B", "w+", encoding="utf-16") roundtrip("foo\r\n", "w+", newline="") + def test_copy_from(self): + # A TemporaryFile can be created from another file + from_f = os.path.dirname(os.path.realpath(__file__))+"/copy_from_test.txt" + to_f = tempfile.TemporaryFile(copy_from=from_f) + with open(from_f, "rb") as copied_from: + with open(to_f.name, "rb") as copied_to: + self.assertEqual(copied_from.read(), copied_to.read()) # Helper for test_del_on_shutdown class NulledModules: