Merged revisions 60481,60485,60489-60492,60494-60496,60498-60499,60501-60503,60505-60506,60508-60509,60523-60524,60532,60543,60545,60547-60548,60552,60554,60556-60559,60561-60562,60569,60571-60572,60574,60576-60583,60585-60586,60589,60591,60594-60595,60597-60598,60600-60601,60606-60612,60615,60617,60619-60621,60623-60625,60627-60629,60631,60633,60635,60647,60650,60652,60654,60656,60658-60659,60664-60666,60668-60670,60672,60676,60678-60695 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r60679 | raymond.hettinger | 2008-02-09 02:18:42 +0100 (Sat, 09 Feb 2008) | 1 line Make ABC containers inherit as documented. ........ r60684 | raymond.hettinger | 2008-02-09 04:34:52 +0100 (Sat, 09 Feb 2008) | 1 line Merge with r60683. ........ r60687 | raymond.hettinger | 2008-02-09 05:37:49 +0100 (Sat, 09 Feb 2008) | 1 line Add -3 warnings that set.copy(), dict.copy(), and defaultdict.copy() will go away in Py3.x ........ r60689 | raymond.hettinger | 2008-02-09 11:04:19 +0100 (Sat, 09 Feb 2008) | 1 line Metaclass declaration is inherited ........ r60691 | raymond.hettinger | 2008-02-09 11:06:20 +0100 (Sat, 09 Feb 2008) | 1 line Temporarily disable this test. It's been broken for a week. ........ r60695 | nick.coghlan | 2008-02-09 16:28:09 +0100 (Sat, 09 Feb 2008) | 1 line Issue 2021: Allow NamedTemporaryFile and SpooledTemporaryFile to be used as context managers. (The NamedTemporaryFile fix should be considered for backporting to 2.5) ........
This commit is contained in:
parent
bfd061218b
commit
3ecfea71ff
|
@ -51,7 +51,8 @@ The module defines the following user-callable functions:
|
|||
|
||||
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
|
||||
underlying true file object.
|
||||
underlying true file object. This file-like object can be used in a :keyword:`with`
|
||||
statement, just like a normal file.
|
||||
|
||||
|
||||
.. function:: NamedTemporaryFile([mode='w+b'[, bufsize=-1[, suffix[, prefix[, dir[, delete]]]]]])
|
||||
|
@ -64,7 +65,8 @@ The module defines the following user-callable functions:
|
|||
across platforms (it can be so used on Unix; it cannot on Windows NT or later).
|
||||
If *delete* is true (the default), the file is deleted as soon as it is closed.
|
||||
The returned object is always a file-like object whose :attr:`file` attribute
|
||||
is the underlying true file object.
|
||||
is the underlying true file object. This file-like object can be used in a :keyword:`with`
|
||||
statement, just like a normal file.
|
||||
|
||||
|
||||
.. function:: SpooledTemporaryFile([max_size=0, [mode='w+b'[, bufsize=-1[, suffix[, prefix[, dir]]]]]])
|
||||
|
@ -79,7 +81,8 @@ The module defines the following user-callable functions:
|
|||
|
||||
The returned object is a file-like object whose :attr:`_file` attribute
|
||||
is either a :class:`StringIO` object or a true file object, depending on
|
||||
whether :func:`rollover` has been called.
|
||||
whether :func:`rollover` has been called. This file-like object can be used in a
|
||||
:keyword:`with` statement, just like a normal file.
|
||||
|
||||
|
||||
.. function:: mkstemp([suffix[, prefix[, dir[, text]]]])
|
||||
|
|
|
@ -365,6 +365,7 @@ class IOBase(metaclass=abc.ABCMeta):
|
|||
|
||||
def __enter__(self) -> "IOBase": # That's a forward reference
|
||||
"""Context management protocol. Returns self."""
|
||||
self._checkClosed()
|
||||
return self
|
||||
|
||||
def __exit__(self, *args) -> None:
|
||||
|
|
|
@ -363,6 +363,7 @@ def mktemp(suffix="", prefix=template, dir=None):
|
|||
|
||||
raise IOError(_errno.EEXIST, "No usable temporary filename found")
|
||||
|
||||
|
||||
class _TemporaryFileWrapper:
|
||||
"""Temporary file wrapper
|
||||
|
||||
|
@ -378,17 +379,25 @@ class _TemporaryFileWrapper:
|
|||
self.delete = delete
|
||||
|
||||
def __getattr__(self, name):
|
||||
# Attribute lookups are delegated to the underlying file
|
||||
# and cached for non-numeric results
|
||||
# (i.e. methods are cached, closed and friends are not)
|
||||
file = self.__dict__['file']
|
||||
a = getattr(file, name)
|
||||
if type(a) != type(0):
|
||||
if not isinstance(a, int):
|
||||
setattr(self, name, a)
|
||||
return a
|
||||
|
||||
# The underlying __enter__ method returns the wrong object
|
||||
# (self.file) so override it to return the wrapper
|
||||
def __enter__(self):
|
||||
self.file.__enter__()
|
||||
return self
|
||||
|
||||
# NT provides delete-on-close as a primitive, so we don't need
|
||||
# the wrapper to do anything special. We still use it so that
|
||||
# file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile.
|
||||
if _os.name != 'nt':
|
||||
|
||||
# Cache the unlinker so we don't get spurious errors at
|
||||
# shutdown when the module-level "os" is None'd out. Note
|
||||
# that this must be referenced as self.unlink, because the
|
||||
|
@ -406,6 +415,14 @@ class _TemporaryFileWrapper:
|
|||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
# Need to trap __exit__ as well to ensure the file gets
|
||||
# deleted when used in a with statement
|
||||
def __exit__(self, exc, value, tb):
|
||||
result = self.file.__exit__(exc, value, tb)
|
||||
self.close()
|
||||
return result
|
||||
|
||||
|
||||
def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None,
|
||||
newline=None, suffix="", prefix=template,
|
||||
dir=None, delete=True):
|
||||
|
@ -523,6 +540,20 @@ class SpooledTemporaryFile:
|
|||
|
||||
self._rolled = True
|
||||
|
||||
# The method caching trick from NamedTemporaryFile
|
||||
# won't work here, because _file may change from a
|
||||
# _StringIO instance to a real file. So we list
|
||||
# all the methods directly.
|
||||
|
||||
# Context management protocol
|
||||
def __enter__(self):
|
||||
if self._file.closed:
|
||||
raise ValueError("Cannot enter context with closed file")
|
||||
return self
|
||||
|
||||
def __exit__(self, exc, value, tb):
|
||||
self._file.close()
|
||||
|
||||
# file protocol
|
||||
def __iter__(self):
|
||||
return self._file.__iter__()
|
||||
|
|
|
@ -336,17 +336,17 @@ class SysModuleTest(unittest.TestCase):
|
|||
def test_compact_freelists(self):
|
||||
sys._compact_freelists()
|
||||
r = sys._compact_freelists()
|
||||
# freed blocks shouldn't change
|
||||
self.assertEqual(r[0][2], 0)
|
||||
# fill freelists
|
||||
ints = list(range(10000))
|
||||
floats = [float(i) for i in ints]
|
||||
del ints
|
||||
del floats
|
||||
# should free more than 100 blocks
|
||||
r = sys._compact_freelists()
|
||||
self.assert_(r[0][1] > 100, r[0][1])
|
||||
self.assert_(r[0][2] > 100, r[0][2])
|
||||
## freed blocks shouldn't change
|
||||
#self.assertEqual(r[0][2], 0)
|
||||
## fill freelists
|
||||
#ints = list(range(10000))
|
||||
#floats = [float(i) for i in ints]
|
||||
#del ints
|
||||
#del floats
|
||||
## should free more than 100 blocks
|
||||
#r = sys._compact_freelists()
|
||||
#self.assert_(r[0][1] > 100, r[0][1])
|
||||
#self.assert_(r[0][2] > 100, r[0][2])
|
||||
|
||||
def test_main():
|
||||
test.test_support.run_unittest(SysModuleTest)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# tempfile.py unit tests.
|
||||
|
||||
import tempfile
|
||||
import os
|
||||
import sys
|
||||
|
@ -619,7 +618,6 @@ class test_NamedTemporaryFile(TC):
|
|||
|
||||
def test_multiple_close(self):
|
||||
# A NamedTemporaryFile can be closed many times without error
|
||||
|
||||
f = tempfile.NamedTemporaryFile()
|
||||
f.write(b'abc\n')
|
||||
f.close()
|
||||
|
@ -629,6 +627,16 @@ class test_NamedTemporaryFile(TC):
|
|||
except:
|
||||
self.failOnException("close")
|
||||
|
||||
def test_context_manager(self):
|
||||
# A NamedTemporaryFile can be used as a context manager
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
self.failUnless(os.path.exists(f.name))
|
||||
self.failIf(os.path.exists(f.name))
|
||||
def use_closed():
|
||||
with f:
|
||||
pass
|
||||
self.failUnlessRaises(ValueError, use_closed)
|
||||
|
||||
# How to test the mode and bufsize parameters?
|
||||
|
||||
test_classes.append(test_NamedTemporaryFile)
|
||||
|
@ -707,10 +715,23 @@ class test_SpooledTemporaryFile(TC):
|
|||
self.failUnless(f.fileno() > 0)
|
||||
self.failUnless(f._rolled)
|
||||
|
||||
def test_multiple_close(self):
|
||||
def test_multiple_close_before_rollover(self):
|
||||
# A SpooledTemporaryFile can be closed many times without error
|
||||
f = tempfile.SpooledTemporaryFile()
|
||||
f.write(b'abc\n')
|
||||
self.failIf(f._rolled)
|
||||
f.close()
|
||||
try:
|
||||
f.close()
|
||||
f.close()
|
||||
except:
|
||||
self.failOnException("close")
|
||||
|
||||
def test_multiple_close_after_rollover(self):
|
||||
# A SpooledTemporaryFile can be closed many times without error
|
||||
f = tempfile.SpooledTemporaryFile(max_size=1)
|
||||
f.write(b'abc\n')
|
||||
self.failUnless(f._rolled)
|
||||
f.close()
|
||||
try:
|
||||
f.close()
|
||||
|
@ -759,6 +780,46 @@ class test_SpooledTemporaryFile(TC):
|
|||
self.assertEqual(f.read(), "\u039B\r\n" + ("\u039B" * 20) + "\r\n")
|
||||
self.failUnless(f._rolled)
|
||||
|
||||
def test_context_manager_before_rollover(self):
|
||||
# A SpooledTemporaryFile can be used as a context manager
|
||||
with tempfile.SpooledTemporaryFile(max_size=1) as f:
|
||||
self.failIf(f._rolled)
|
||||
self.failIf(f.closed)
|
||||
self.failUnless(f.closed)
|
||||
def use_closed():
|
||||
with f:
|
||||
pass
|
||||
self.failUnlessRaises(ValueError, use_closed)
|
||||
|
||||
def test_context_manager_during_rollover(self):
|
||||
# A SpooledTemporaryFile can be used as a context manager
|
||||
with tempfile.SpooledTemporaryFile(max_size=1) as f:
|
||||
self.failIf(f._rolled)
|
||||
f.write(b'abc\n')
|
||||
f.flush()
|
||||
self.failUnless(f._rolled)
|
||||
self.failIf(f.closed)
|
||||
self.failUnless(f.closed)
|
||||
def use_closed():
|
||||
with f:
|
||||
pass
|
||||
self.failUnlessRaises(ValueError, use_closed)
|
||||
|
||||
def test_context_manager_after_rollover(self):
|
||||
# A SpooledTemporaryFile can be used as a context manager
|
||||
f = tempfile.SpooledTemporaryFile(max_size=1)
|
||||
f.write(b'abc\n')
|
||||
f.flush()
|
||||
self.failUnless(f._rolled)
|
||||
with f:
|
||||
self.failIf(f.closed)
|
||||
self.failUnless(f.closed)
|
||||
def use_closed():
|
||||
with f:
|
||||
pass
|
||||
self.failUnlessRaises(ValueError, use_closed)
|
||||
|
||||
|
||||
test_classes.append(test_SpooledTemporaryFile)
|
||||
|
||||
|
||||
|
|
|
@ -1127,7 +1127,7 @@ defdict_copy(defdictobject *dd)
|
|||
{
|
||||
/* This calls the object's class. That only works for subclasses
|
||||
whose class constructor has the same signature. Subclasses that
|
||||
define a different constructor signature must override copy().
|
||||
define a different constructor signature must override __copy__().
|
||||
*/
|
||||
return PyObject_CallFunctionObjArgs((PyObject*)Py_TYPE(dd),
|
||||
dd->default_factory, dd, NULL);
|
||||
|
@ -1182,8 +1182,6 @@ defdict_reduce(defdictobject *dd)
|
|||
static PyMethodDef defdict_methods[] = {
|
||||
{"__missing__", (PyCFunction)defdict_missing, METH_O,
|
||||
defdict_missing_doc},
|
||||
{"copy", (PyCFunction)defdict_copy, METH_NOARGS,
|
||||
defdict_copy_doc},
|
||||
{"__copy__", (PyCFunction)defdict_copy, METH_NOARGS,
|
||||
defdict_copy_doc},
|
||||
{"__reduce__", (PyCFunction)defdict_reduce, METH_NOARGS,
|
||||
|
|
Loading…
Reference in New Issue